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

Commit 660b219c authored by Anirudh Ghayal's avatar Anirudh Ghayal
Browse files

regulator: qpnp-labibb: Add support for TTW on PMI8950



Add TTW configuration for PMI8950. Also, add a DT property
(qcom,labibb-ttw-force-lab-on) to keep LAB always-on in TTW mode.
Presently, this property is only required for PMI8950.

CRs-Fixed: 958285 962662
Change-Id: Iec79a6f752067d96fc62a8e9d629c39f4db5ab9f
Signed-off-by: default avatarAnirudh Ghayal <aghayal@codeaurora.org>
parent 0dbc4061
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ Main node required properties:
				configured for AMOLED mode together.
				"stand-alone" means using LAB and IBB regulators
				as stand alone regulators.
- qcom,pmic-revid:		Specifies the phandle of the PMIC revid module.
				Used to identify the PMIC subtype.

Main node optional properties:

@@ -31,6 +33,8 @@ Main node optional properties:
- qpnp,swire-control:			A bool property which indicates if the LAB/IBB is
					controlled by the SWIRE interface. Enable only
					if qpnp,qpnp-labibb-mode = "amoled".
- qcom,labibb-ttw-force-lab-on:		A boolean property which forces LAB to be
					always on during TTW mode.

LAB subnode required properties:

@@ -200,6 +204,7 @@ Example:
			#address-cells = <1>;
			#size-cells = <1>;
			qpnp,qpnp-labibb-mode = "lcd";
			qcom,pmic-revid = <&pmi8994_revid>;

			lab_regulator: qcom,lab@de00 {
				reg = <0xde00 0x100>;
+1 −0
Original line number Diff line number Diff line
@@ -428,6 +428,7 @@
			compatible = "qcom,qpnp-labibb-regulator";
			#address-cells = <1>;
			#size-cells = <1>;
			qcom,pmic-revid = <&pmi8950_revid>;

			ibb_regulator: qcom,ibb@dc00 {
				reg = <0xdc00 0x100>;
+1 −0
Original line number Diff line number Diff line
@@ -452,6 +452,7 @@
			compatible = "qcom,qpnp-labibb-regulator";
			#address-cells = <1>;
			#size-cells = <1>;
			qcom,pmic-revid = <&pmi8994_revid>;

			ibb_regulator: qcom,ibb@dc00 {
				reg = <0xdc00 0x100>;
+292 −76
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/of_regulator.h>
#include <linux/qpnp/qpnp-revid.h>

#define QPNP_LABIBB_REGULATOR_DRIVER_NAME	"qcom,qpnp-labibb-regulator"

@@ -108,6 +109,8 @@
#define LAB_RDSON_MNGMNT_NFET_SHIFT	2
#define LAB_RDSON_MNGMNT_PFET_BITS	2
#define LAB_RDSON_MNGMNT_PFET_MASK	((1 << LAB_RDSON_MNGMNT_PFET_BITS) - 1)
#define LAB_RDSON_NFET_SW_SIZE_QUARTER	0x0
#define LAB_RDSON_PFET_SW_SIZE_QUARTER	0x0

/* REG_LAB_PRECHARGE_CTL */
#define LAB_PRECHARGE_CTL_EN		BIT(2)
@@ -193,6 +196,9 @@
#define IBB_PFET_SW_SIZE_MASK		((1 << PFET_SW_SIZE_BITS) - 1)
#define IBB_NFET_SW_SIZE_SHIFT		3

/* REG_IBB_SOFT_START_CTL */
#define IBB_SOFT_START_CHARGING_RESISTOR_16K	0x3

/* REG_IBB_SPARE_CTL */
#define IBB_BYPASS_PWRDN_DLY2_BIT	BIT(5)
#define IBB_FAST_STARTUP		BIT(3)
@@ -226,6 +232,12 @@
#define IBB_DIS_DLY_MASK		((1 << IBB_DIS_DLY_BITS) - 1)
#define IBB_WAIT_MBG_OK			BIT(2)

enum pmic_subtype {
	PMI8994		= 10,
	PMI8950		= 17,
	PMI8996		= 19,
};

/**
 * enum qpnp_labibb_mode - working mode of LAB/IBB regulators
 * %QPNP_LABIBB_STANDALONE_MODE:	configure LAB/IBB regulator as a
@@ -441,6 +453,7 @@ struct ibb_regulator {
struct qpnp_labibb {
	struct device			*dev;
	struct spmi_device		*spmi;
	struct pmic_revid_data		*pmic_rev_id;
	u16				lab_base;
	u16				ibb_base;
	struct lab_regulator		lab_vreg;
@@ -450,6 +463,7 @@ struct qpnp_labibb {
	bool				in_ttw_mode;
	bool				ibb_settings_saved;
	bool				swire_control;
	bool				ttw_force_lab_on;
};

enum ibb_settings_index {
@@ -458,11 +472,16 @@ enum ibb_settings_index {
	IBB_RDSON_MNGMNT,
	IBB_PWRUP_PWRDN_CTL_1,
	IBB_PWRUP_PWRDN_CTL_2,
	IBB_NLIMIT_DAC,
	IBB_PS_CTL,
	IBB_SOFT_START_CTL,
	IBB_SETTINGS_MAX,
};

enum lab_settings_index {
	LAB_SOFT_START_CTL = 0,
	LAB_PS_CTL,
	LAB_RDSON_MNGMNT,
	LAB_SETTINGS_MAX,
};

@@ -484,10 +503,15 @@ static struct settings ibb_settings[IBB_SETTINGS_MAX] = {
	SETTING(IBB_RDSON_MNGMNT, false),
	SETTING(IBB_PWRUP_PWRDN_CTL_1, true),
	SETTING(IBB_PWRUP_PWRDN_CTL_2, true),
	SETTING(IBB_NLIMIT_DAC, false),
	SETTING(IBB_PS_CTL, false),
	SETTING(IBB_SOFT_START_CTL, false),
};

static struct settings lab_settings[LAB_SETTINGS_MAX] = {
	SETTING(LAB_SOFT_START_CTL, false),
	SETTING(LAB_PS_CTL, false),
	SETTING(LAB_RDSON_MNGMNT, false),
};

static int
@@ -960,48 +984,11 @@ static int qpnp_labibb_save_settings(struct qpnp_labibb *labibb)
	return 0;
}

static int qpnp_labibb_regulator_ttw_mode_enter(struct qpnp_labibb *labibb)
static int qpnp_labibb_ttw_enter_ibb_common(struct qpnp_labibb *labibb)
{
	int rc = 0;
	u8 val;

	/* Save the IBB settings before they get modified for TTW mode */
	if (!labibb->ibb_settings_saved) {
		rc = qpnp_labibb_save_settings(labibb);
		if (rc) {
			pr_err("Error in storing IBB setttings, rc=%d\n", rc);
			return rc;
		}
		labibb->ibb_settings_saved = true;
	}

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

	val = LAB_SPARE_TOUCH_WAKE_BIT | LAB_SPARE_DISABLE_SCP_BIT;
	rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_SPARE_CTL,
				&val, 1);
	if (rc) {
		pr_err("qpnp_labibb_write register %x failed rc = %d\n",
			REG_LAB_SPARE_CTL, rc);
		return rc;
	}

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

	val = 0;
	rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_PD_CTL,
				&val, 1);
@@ -1044,18 +1031,171 @@ static int qpnp_labibb_regulator_ttw_mode_enter(struct qpnp_labibb *labibb)
		(IBB_ILIMIT_COUNT_CYC8 << IBB_CURRENT_LIMIT_DEBOUNCE_SHIFT);
	rc = qpnp_labibb_sec_write(labibb, labibb->ibb_base,
				REG_IBB_CURRENT_LIMIT, &val, 1);
	if (rc) {
	if (rc)
		pr_err("qpnp_labibb_write register %x failed rc = %d\n",
			REG_IBB_CURRENT_LIMIT, rc);

	return rc;
}

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

	val = IBB_BYPASS_PWRDN_DLY2_BIT | IBB_FAST_STARTUP;
	rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_SPARE_CTL,
				&val, 1);
	if (rc) {
	if (rc)
		pr_err("qpnp_labibb_write register %x failed rc = %d\n",
			REG_IBB_SPARE_CTL, rc);

	return rc;
}

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

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

	val = IBB_PS_CTL_EN;
	rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_PS_CTL,
				&val, 1);
	if (rc) {
		pr_err("qpnp_labibb_write register %x failed rc = %d\n",
			REG_IBB_PS_CTL, rc);
		return rc;
	}

	val = IBB_SOFT_START_CHARGING_RESISTOR_16K;
	rc = qpnp_labibb_write(labibb, labibb->ibb_base +
				REG_IBB_SOFT_START_CTL, &val, 1);
	if (rc) {
		pr_err("qpnp_labibb_write register %x failed rc = %d\n",
			REG_IBB_SOFT_START_CTL, rc);
		return rc;
	}

	val = IBB_MODULE_RDY_EN;
	rc = qpnp_labibb_write(labibb, labibb->lab_base +
				REG_IBB_MODULE_RDY, &val, 1);
	if (rc)
		pr_err("qpnp_labibb_write register %x failed rc = %d\n",
				REG_IBB_MODULE_RDY, rc);

	return rc;
}

static int qpnp_labibb_regulator_ttw_mode_enter(struct qpnp_labibb *labibb)
{
	int rc = 0;
	u8 val;

	/* Save the IBB settings before they get modified for TTW mode */
	if (!labibb->ibb_settings_saved) {
		rc = qpnp_labibb_save_settings(labibb);
		if (rc) {
			pr_err("Error in storing IBB setttings, rc=%d\n", rc);
			return rc;
		}
		labibb->ibb_settings_saved = true;
	}

	if (labibb->ttw_force_lab_on) {
		val = LAB_MODULE_RDY_EN;
		rc = qpnp_labibb_write(labibb, labibb->lab_base +
					REG_LAB_MODULE_RDY, &val, 1);
		if (rc) {
			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
				REG_LAB_MODULE_RDY, rc);
			return rc;
		}

		/* Prevents LAB being turned off by IBB */
		val = LAB_ENABLE_CTL_EN;
		rc = qpnp_labibb_write(labibb, labibb->lab_base +
					REG_LAB_ENABLE_CTL, &val, 1);
		if (rc) {
			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
				REG_LAB_ENABLE_CTL, rc);
			return rc;
		}

		val = LAB_RDSON_MNGMNT_NFET_SLEW_EN |
			LAB_RDSON_MNGMNT_PFET_SLEW_EN |
			LAB_RDSON_NFET_SW_SIZE_QUARTER |
			LAB_RDSON_PFET_SW_SIZE_QUARTER;
		rc = qpnp_labibb_write(labibb, labibb->lab_base +
				REG_LAB_RDSON_MNGMNT, &val, 1);
		if (rc) {
			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
				REG_LAB_RDSON_MNGMNT, rc);
			return rc;
		}

		rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
				REG_LAB_PS_CTL, LAB_PS_CTL_EN, LAB_PS_CTL_EN);
		if (rc) {
			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
				REG_LAB_PS_CTL, rc);
			return rc;
		}
	} else {
		val = LAB_PD_CTL_DISABLE_PD;
		rc = qpnp_labibb_write(labibb, labibb->lab_base +
				REG_LAB_PD_CTL, &val, 1);
		if (rc) {
			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
				REG_LAB_PD_CTL, rc);
			return rc;
		}

		val = LAB_SPARE_DISABLE_SCP_BIT;
		if (labibb->pmic_rev_id->pmic_subtype != PMI8950)
			val |= LAB_SPARE_TOUCH_WAKE_BIT;
		rc = qpnp_labibb_write(labibb, labibb->lab_base +
				REG_LAB_SPARE_CTL, &val, 1);
		if (rc) {
			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
				REG_LAB_SPARE_CTL, rc);
			return rc;
		}

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

	rc = qpnp_labibb_ttw_enter_ibb_common(labibb);
	if (rc) {
		pr_err("Failed to apply TTW ibb common settings rc=%d\n", rc);
		return rc;
	}

	switch (labibb->pmic_rev_id->pmic_subtype) {
	case PMI8996:
		rc = qpnp_labibb_ttw_enter_ibb_pmi8996(labibb);
		break;
	case PMI8950:
		rc = qpnp_labibb_ttw_enter_ibb_pmi8950(labibb);
		break;
	}
	if (rc) {
		pr_err("Failed to configure TTW-enter for IBB rc=%d\n", rc);
		return rc;
	}

@@ -1068,6 +1208,21 @@ static int qpnp_labibb_regulator_ttw_mode_enter(struct qpnp_labibb *labibb)
	return 0;
}

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

	val = 0;
	rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_SPARE_CTL,
			&val, 1);
	if (rc)
		pr_err("qpnp_labibb_write register %x failed rc = %d\n",
			REG_IBB_SPARE_CTL, rc);

	return rc;
}

static int qpnp_labibb_regulator_ttw_mode_exit(struct qpnp_labibb *labibb)
{
	int rc = 0;
@@ -1085,9 +1240,19 @@ static int qpnp_labibb_regulator_ttw_mode_exit(struct qpnp_labibb *labibb)
		return rc;
	}

	if (labibb->ttw_force_lab_on) {
		val = 0;
		rc = qpnp_labibb_write(labibb, labibb->lab_base +
					REG_LAB_ENABLE_CTL, &val, 1);
		if (rc) {
			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
				REG_LAB_ENABLE_CTL, rc);
			return rc;
		}
	} else {
		val = LAB_PD_CTL_STRONG_PULL;
	rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_PD_CTL,
				&val, 1);
		rc = qpnp_labibb_write(labibb, labibb->lab_base +
					REG_LAB_PD_CTL,	&val, 1);
		if (rc) {
			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
						REG_LAB_PD_CTL, rc);
@@ -1095,20 +1260,22 @@ static int qpnp_labibb_regulator_ttw_mode_exit(struct qpnp_labibb *labibb)
		}

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

	val = 0;
	rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_SPARE_CTL,
				&val, 1);
	switch (labibb->pmic_rev_id->pmic_subtype) {
	case PMI8996:
		rc = qpnp_labibb_ttw_exit_ibb_pmi8996(labibb);
		break;
	}
	if (rc) {
		pr_err("qpnp_labibb_write register %x failed rc = %d\n",
			REG_IBB_SPARE_CTL, rc);
		pr_err("Failed to configure TTW-exit for IBB rc=%d\n", rc);
		return rc;
	}

@@ -2171,20 +2338,6 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb,
		return rc;
	}

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

	/* PMI8996 has revision 1 */
	if (val < 1 && labibb->ttw_en) {
		pr_err("TTW feature cannot be enabled for revision %d\n", val);
		labibb->ttw_en = false;
	}

	if (of_find_property(of_node, "qcom,output-voltage-one-pulse", NULL)) {
		if (!labibb->swire_control) {
			pr_err("Invalid property 'qcom,output-voltage-one-pulse', valid only in SWIRE config\n");
@@ -2359,11 +2512,49 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb,
	return 0;
}

static int qpnp_labibb_check_ttw_supported(struct qpnp_labibb *labibb)
{
	int rc = 0;
	u8 val;

	switch (labibb->pmic_rev_id->pmic_subtype) {
	case PMI8996:
		rc = qpnp_labibb_read(labibb, &val,
				labibb->ibb_base + REG_IBB_REVISION4, 1);
		if (rc) {
			pr_err("qpnp_labibb_read register %x failed rc = %d\n",
				REG_IBB_REVISION4, rc);
			return rc;
		}

		/* PMI8996 has revision 1 */
		if (val < 1) {
			pr_err("TTW feature cannot be enabled for revision %d\n",
									val);
			labibb->ttw_en = false;
		}
		/* FORCE_LAB_ON in TTW is not required for PMI8996 */
		labibb->ttw_force_lab_on = false;
		break;
	case PMI8950:
		/* TTW supported for all revisions */
		break;
	default:
		pr_info("TTW mode not supported for PMIC-subtype = %d\n",
					labibb->pmic_rev_id->pmic_subtype);
		labibb->ttw_en = false;
		break;

	}
	return rc;
}

static int qpnp_labibb_regulator_probe(struct spmi_device *spmi)
{
	struct qpnp_labibb *labibb;
	struct resource *resource;
	struct spmi_resource *spmi_resource;
	struct device_node *revid_dev_node;
	const char *mode_name;
	u8 type;
	int rc = 0;
@@ -2378,6 +2569,19 @@ static int qpnp_labibb_regulator_probe(struct spmi_device *spmi)
	labibb->dev = &(spmi->dev);
	labibb->spmi = spmi;

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

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

	rc = of_property_read_string(labibb->dev->of_node,
			"qpnp,qpnp-labibb-mode", &mode_name);
	if (!rc) {
@@ -2404,6 +2608,9 @@ static int qpnp_labibb_regulator_probe(struct spmi_device *spmi)
		return -EINVAL;
	}

	labibb->ttw_force_lab_on = of_property_read_bool(
		labibb->dev->of_node, "qcom,labibb-ttw-force-lab-on");

	labibb->swire_control = of_property_read_bool(labibb->dev->of_node,
							"qpnp,swire-control");
	if (labibb->swire_control && labibb->mode != QPNP_LABIBB_AMOLED_MODE) {
@@ -2457,6 +2664,15 @@ static int qpnp_labibb_regulator_probe(struct spmi_device *spmi)
		}
	}

	if (labibb->ttw_en) {
		rc = qpnp_labibb_check_ttw_supported(labibb);
		if (rc) {
			pr_err("pmic revision check failed for TTW rc=%d\n",
									rc);
			goto fail_registration;
		}
	}

	dev_set_drvdata(&spmi->dev, labibb);
	return 0;