regulator: qpnp-lcdb: Add a WA to toggle SC before module_enable

There is a possibility of the NCP failing to turn on due
to an invalid (short circuit) SC event before the LCDB module
is enabled.

Force a SC re-enable to recover from this condition. Enable this
for PM660L V1.1.

Also, disable the SC handling logic for PM660L to avoid
permanently disabling the module due to the above mentioned
issue.

CRs-Fixed: 2025449
Change-Id: I191d11c35c3d96727874818b8b57fa1c254879bf
Signed-off-by: Anirudh Ghayal <aghayal@codeaurora.org>
This commit is contained in:
Anirudh Ghayal 2017-03-31 15:34:05 +05:30
parent 5549356bec
commit 1054280e99
3 changed files with 92 additions and 33 deletions

View file

@ -26,12 +26,10 @@ First Level Node - LCDB module
Value type: <prop-encoded-array>
Definition: Base address of the LCDB SPMI peripheral.
- qcom,force-module-reenable
Usage: required if using SW mode for module enable
Value type: <bool>
Definition: This enables the workaround to force enable
the vph_pwr_2p5_ok signal required for
turning on the LCDB module.
- qcom,pmic-revid
Usage: required
Value type: <phandle>
Definition: Phandle to the PMIC's revid node
Touch-to-wake (TTW) properties:

View file

@ -397,7 +397,7 @@
interrupts = <0x3 0xec 0x1 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "sc-irq";
qcom,force-module-reenable;
qcom,pmic-revid = <&pm660l_revid>;
lcdb_ldo_vreg: ldo {
label = "ldo";

View file

@ -24,6 +24,7 @@
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/machine.h>
#include <linux/qpnp/qpnp-revid.h>
#define QPNP_LCDB_REGULATOR_DRIVER_NAME "qcom,qpnp-lcdb-regulator"
@ -192,6 +193,7 @@ struct qpnp_lcdb {
struct device *dev;
struct platform_device *pdev;
struct regmap *regmap;
struct pmic_revid_data *pmic_rev_id;
u32 base;
int sc_irq;
@ -199,9 +201,6 @@ struct qpnp_lcdb {
bool ttw_enable;
bool ttw_mode_sw;
/* top level DT params */
bool force_module_reenable;
/* status parameters */
bool lcdb_enabled;
bool settings_saved;
@ -579,6 +578,65 @@ static int qpnp_lcdb_ttw_exit(struct qpnp_lcdb *lcdb)
return 0;
}
static int qpnp_lcdb_enable_wa(struct qpnp_lcdb *lcdb)
{
int rc;
u8 val = 0;
/* required only for PM660L */
if (lcdb->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE)
return 0;
val = MODULE_EN_BIT;
rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_ENABLE_CTL1_REG,
&val, 1);
if (rc < 0) {
pr_err("Failed to enable lcdb rc= %d\n", rc);
return rc;
}
val = 0;
rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_ENABLE_CTL1_REG,
&val, 1);
if (rc < 0) {
pr_err("Failed to disable lcdb rc= %d\n", rc);
return rc;
}
/* execute the below for rev1.1 */
if (lcdb->pmic_rev_id->rev3 == PM660L_V1P1_REV3 &&
lcdb->pmic_rev_id->rev4 == PM660L_V1P1_REV4) {
/*
* delay to make sure that the MID pin ie the
* output of the LCDB boost returns to 0V
* after the module is disabled
*/
usleep_range(10000, 10100);
rc = qpnp_lcdb_masked_write(lcdb,
lcdb->base + LCDB_MISC_CTL_REG,
DIS_SCP_BIT, DIS_SCP_BIT);
if (rc < 0) {
pr_err("Failed to disable SC rc=%d\n", rc);
return rc;
}
/* delay for SC-disable to take effect */
usleep_range(1000, 1100);
rc = qpnp_lcdb_masked_write(lcdb,
lcdb->base + LCDB_MISC_CTL_REG,
DIS_SCP_BIT, 0);
if (rc < 0) {
pr_err("Failed to enable SC rc=%d\n", rc);
return rc;
}
/* delay for SC-enable to take effect */
usleep_range(1000, 1100);
}
return 0;
}
static int qpnp_lcdb_enable(struct qpnp_lcdb *lcdb)
{
int rc = 0, timeout, delay;
@ -598,31 +656,20 @@ static int qpnp_lcdb_enable(struct qpnp_lcdb *lcdb)
}
}
rc = qpnp_lcdb_enable_wa(lcdb);
if (rc < 0) {
pr_err("Failed to execute enable_wa rc=%d\n", rc);
return rc;
}
val = MODULE_EN_BIT;
rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_ENABLE_CTL1_REG,
&val, 1);
if (rc < 0) {
pr_err("Failed to enable lcdb rc= %d\n", rc);
pr_err("Failed to disable lcdb rc= %d\n", rc);
goto fail_enable;
}
if (lcdb->force_module_reenable) {
val = 0;
rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_ENABLE_CTL1_REG,
&val, 1);
if (rc < 0) {
pr_err("Failed to enable lcdb rc= %d\n", rc);
goto fail_enable;
}
val = MODULE_EN_BIT;
rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_ENABLE_CTL1_REG,
&val, 1);
if (rc < 0) {
pr_err("Failed to disable lcdb rc= %d\n", rc);
goto fail_enable;
}
}
/* poll for vreg_ok */
timeout = 10;
delay = lcdb->bst.soft_start_us + lcdb->ldo.soft_start_us +
@ -1674,7 +1721,8 @@ static int qpnp_lcdb_hw_init(struct qpnp_lcdb *lcdb)
return rc;
}
if (lcdb->sc_irq >= 0) {
if (lcdb->sc_irq >= 0 &&
lcdb->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE) {
lcdb->sc_count = 0;
rc = devm_request_threaded_irq(lcdb->dev, lcdb->sc_irq,
NULL, qpnp_lcdb_sc_irq_handler, IRQF_ONESHOT,
@ -1714,7 +1762,23 @@ static int qpnp_lcdb_parse_dt(struct qpnp_lcdb *lcdb)
{
int rc = 0;
const char *label;
struct device_node *temp, *node = lcdb->dev->of_node;
struct device_node *revid_dev_node, *temp, *node = lcdb->dev->of_node;
revid_dev_node = of_parse_phandle(node, "qcom,pmic-revid", 0);
if (!revid_dev_node) {
pr_err("Missing qcom,pmic-revid property - fail driver\n");
return -EINVAL;
}
lcdb->pmic_rev_id = get_revid_data(revid_dev_node);
if (IS_ERR(lcdb->pmic_rev_id)) {
pr_debug("Unable to get revid data\n");
/*
* revid should to be defined, return -EPROBE_DEFER
* until the revid module registers.
*/
return -EPROBE_DEFER;
}
for_each_available_child_of_node(node, temp) {
rc = of_property_read_string(temp, "label", &label);
@ -1742,9 +1806,6 @@ static int qpnp_lcdb_parse_dt(struct qpnp_lcdb *lcdb)
}
}
lcdb->force_module_reenable = of_property_read_bool(node,
"qcom,force-module-reenable");
if (of_property_read_bool(node, "qcom,ttw-enable")) {
rc = qpnp_lcdb_parse_ttw(lcdb);
if (rc < 0) {