From 37265b612324668ff80d5f32e87d093bbac8761f Mon Sep 17 00:00:00 2001 From: Kiran Gunda Date: Wed, 23 Jan 2019 13:12:06 +0530 Subject: [PATCH] regulator: qpnp-lcdb: Add a DT property for LDO power-down delay Add "qcom,pwrdn-delay-ms" device tree property to meet the power down delay spec for few display panels. Also, enable force-pull down on module disable to do a quick discharge of the LDO. Change-Id: I07d55cdbfe3b253579ca3722fc02604a4a7483e0 Signed-off-by: Kiran Gunda --- .../regulator/qpnp-lcdb-regulator.txt | 6 ++ drivers/regulator/qpnp-lcdb-regulator.c | 96 ++++++++++++++++++- 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt index 6fae4187b052..601a8e73733b 100644 --- a/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt @@ -39,6 +39,12 @@ First Level Node - LCDB module to be enabled only on platforms where voltage needs to be ramped up with multiple steps. +- qcom,pwrdn-delay-ms + Usage: optional + Value type: + Definition: Required to control the LDO power down delay. + Possible values are 0, 1, 4, 8. + Touch-to-wake (TTW) properties: TTW supports 2 modes of operation - HW and SW. In the HW mode the enable/disable diff --git a/drivers/regulator/qpnp-lcdb-regulator.c b/drivers/regulator/qpnp-lcdb-regulator.c index b85a5d619e1b..4d4080d278b3 100644 --- a/drivers/regulator/qpnp-lcdb-regulator.c +++ b/drivers/regulator/qpnp-lcdb-regulator.c @@ -114,6 +114,10 @@ #define PFM_CURRENT_SHIFT 2 #define LCDB_PWRUP_PWRDN_CTL_REG 0x66 +#define PWRUP_DELAY_MASK GENAMSK(3, 2) +#define PWRDN_DELAY_MASK GENMASK(1, 0) +#define PWRDN_DELAY_MIN_MS 0 +#define PWRDN_DELAY_MAX_MS 8 /* LDO */ #define LCDB_LDO_OUTPUT_VOLTAGE_REG 0x71 @@ -126,6 +130,10 @@ #define LDO_DIS_PULLDOWN_BIT BIT(1) #define LDO_PD_STRENGTH_BIT BIT(0) +#define LCDB_LDO_FORCE_PD_CTL_REG 0x79 +#define LDO_FORCE_PD_EN_BIT BIT(0) +#define LDO_FORCE_PD_MODE BIT(7) + #define LCDB_LDO_ILIM_CTL1_REG 0x7B #define EN_LDO_ILIM_BIT BIT(7) #define SET_LDO_ILIM_MASK GENMASK(2, 0) @@ -220,7 +228,9 @@ struct qpnp_lcdb { struct regmap *regmap; struct pmic_revid_data *pmic_rev_id; u32 base; + u32 wa_flags; int sc_irq; + int pwrdn_delay_ms; /* TTW params */ bool ttw_enable; @@ -292,6 +302,11 @@ enum lcdb_settings_index { LCDB_SETTING_MAX, }; +enum lcdb_wa_flags { + NCP_SCP_DISABLE_WA = BIT(0), + FORCE_PD_ENABLE_WA = BIT(1), +}; + static u32 soft_start_us[] = { 0, 500, @@ -313,6 +328,13 @@ static u32 ncp_ilim_ma[] = { 810, }; +static const u32 pwrup_pwrdn_ms[] = { + 0, + 1, + 4, + 8, +}; + #define SETTING(_id, _sec_access, _valid) \ [_id] = { \ .address = _id##_REG, \ @@ -915,6 +937,18 @@ static int qpnp_lcdb_disable(struct qpnp_lcdb *lcdb) return 0; } + if (lcdb->wa_flags & FORCE_PD_ENABLE_WA) { + /* + * force pull-down to enable quick discharge after + * turning off + */ + val = LDO_FORCE_PD_EN_BIT | LDO_FORCE_PD_MODE; + rc = qpnp_lcdb_write(lcdb, lcdb->base + + LCDB_LDO_FORCE_PD_CTL_REG, &val, 1); + if (rc < 0) + return rc; + } + val = 0; rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_ENABLE_CTL1_REG, &val, 1); @@ -923,6 +957,17 @@ static int qpnp_lcdb_disable(struct qpnp_lcdb *lcdb) else lcdb->lcdb_enabled = false; + if (lcdb->wa_flags & FORCE_PD_ENABLE_WA) { + /* wait for 10 msec after module disable for LDO to discharge */ + usleep_range(10000, 11000); + + val = 0; + rc = qpnp_lcdb_write(lcdb, lcdb->base + + LCDB_LDO_FORCE_PD_CTL_REG, &val, 1); + if (rc < 0) + return rc; + } + return rc; } @@ -2020,11 +2065,40 @@ static int qpnp_lcdb_init_bst(struct qpnp_lcdb *lcdb) return 0; } +static void qpnp_lcdb_pmic_config(struct qpnp_lcdb *lcdb) +{ + switch (lcdb->pmic_rev_id->pmic_subtype) { + case PM660L_SUBTYPE: + if (lcdb->pmic_rev_id->rev4 < PM660L_V2P0_REV4) + lcdb->wa_flags |= NCP_SCP_DISABLE_WA; + break; + case PMI632_SUBTYPE: + case PM6150L_SUBTYPE: + lcdb->wa_flags |= FORCE_PD_ENABLE_WA; + break; + default: + break; + } + + pr_debug("LCDB wa_flags = 0x%2x\n", lcdb->wa_flags); +} + static int qpnp_lcdb_hw_init(struct qpnp_lcdb *lcdb) { int rc = 0; u8 val = 0; + qpnp_lcdb_pmic_config(lcdb); + + if (lcdb->pwrdn_delay_ms != -EINVAL) { + rc = qpnp_lcdb_masked_write(lcdb, lcdb->base + + LCDB_PWRUP_PWRDN_CTL_REG, + PWRDN_DELAY_MASK, + lcdb->pwrdn_delay_ms); + if (rc < 0) + return rc; + } + rc = qpnp_lcdb_init_bst(lcdb); if (rc < 0) { pr_err("Failed to initialize BOOST rc=%d\n", rc); @@ -2082,7 +2156,8 @@ static int qpnp_lcdb_hw_init(struct qpnp_lcdb *lcdb) static int qpnp_lcdb_parse_dt(struct qpnp_lcdb *lcdb) { - int rc = 0; + int rc = 0, i = 0; + u32 tmp; const char *label; struct device_node *revid_dev_node, *temp, *node = lcdb->dev->of_node; @@ -2146,7 +2221,24 @@ static int qpnp_lcdb_parse_dt(struct qpnp_lcdb *lcdb) lcdb->voltage_step_ramp = of_property_read_bool(node, "qcom,voltage-step-ramp"); - return rc; + lcdb->pwrdn_delay_ms = -EINVAL; + rc = of_property_read_u32(node, "qcom,pwrdn-delay-ms", &tmp); + if (!rc) { + if (!is_between(tmp, PWRDN_DELAY_MIN_MS, PWRDN_DELAY_MAX_MS)) { + pr_err("Invalid PWRDN_DLY val %d (min=%d max=%d)\n", + tmp, PWRDN_DELAY_MIN_MS, PWRDN_DELAY_MAX_MS); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(pwrup_pwrdn_ms); i++) { + if (tmp == pwrup_pwrdn_ms[i]) { + lcdb->pwrdn_delay_ms = i; + break; + } + } + } + + return 0; } static int qpnp_lcdb_regulator_probe(struct platform_device *pdev)