summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt5
-rw-r--r--arch/arm/boot/dts/qcom/msm-pmi8994.dtsi1
-rw-r--r--drivers/regulator/qpnp-labibb-regulator.c369
3 files changed, 298 insertions, 77 deletions
diff --git a/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
index e19f9b9aef9d..134bfd594994 100644
--- a/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
@@ -18,6 +18,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:
@@ -30,6 +32,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:
@@ -198,6 +202,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>;
diff --git a/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi b/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi
index a851069ad4ff..e0866e0562f6 100644
--- a/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi
+++ b/arch/arm/boot/dts/qcom/msm-pmi8994.dtsi
@@ -441,6 +441,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>;
diff --git a/drivers/regulator/qpnp-labibb-regulator.c b/drivers/regulator/qpnp-labibb-regulator.c
index de4614d4d1ef..83c309bb9089 100644
--- a/drivers/regulator/qpnp-labibb-regulator.c
+++ b/drivers/regulator/qpnp-labibb-regulator.c
@@ -26,6 +26,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"
@@ -110,6 +111,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)
@@ -195,6 +198,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)
@@ -228,6 +234,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
@@ -444,6 +456,7 @@ struct qpnp_labibb {
struct device *dev;
struct platform_device *pdev;
struct regmap *regmap;
+ struct pmic_revid_data *pmic_rev_id;
u16 lab_base;
u16 ibb_base;
struct lab_regulator lab_vreg;
@@ -453,6 +466,7 @@ struct qpnp_labibb {
bool in_ttw_mode;
bool ibb_settings_saved;
bool swire_control;
+ bool ttw_force_lab_on;
};
enum ibb_settings_index {
@@ -461,11 +475,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,
};
@@ -487,10 +506,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
@@ -950,48 +974,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);
@@ -1034,18 +1021,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;
- }
+
+ 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;
}
@@ -1058,6 +1198,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;
@@ -1075,30 +1230,42 @@ static int qpnp_labibb_regulator_ttw_mode_exit(struct qpnp_labibb *labibb)
return rc;
}
- val = LAB_PD_CTL_STRONG_PULL;
- 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;
- }
+ 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);
+ if (rc) {
+ pr_err("qpnp_labibb_write register %x failed rc = %d\n",
+ REG_LAB_PD_CTL, rc);
+ return rc;
+ }
- val = 0;
- 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_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;
}
@@ -2138,20 +2305,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");
@@ -2317,11 +2470,48 @@ 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 platform_device *pdev)
{
struct qpnp_labibb *labibb;
unsigned int base;
- struct device_node *child;
+ struct device_node *child, *revid_dev_node;
const char *mode_name;
u8 type;
int rc = 0;
@@ -2341,6 +2531,19 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
labibb->dev = &(pdev->dev);
labibb->pdev = pdev;
+ revid_dev_node = of_parse_phandle(labibb->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) {
@@ -2367,6 +2570,9 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
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) {
@@ -2417,7 +2623,16 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
}
}
+ 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(&pdev->dev, labibb);
+
return 0;
fail_registration: