regulator: qpnp-labibb: Handle LAB short circuit(SC) detection

On certain PMICs, there is no short circuit (SC) detection available
in LAB module. Add support to detect it manually and disable LAB/IBB
module upon SC detection. In addition, notify it to OLEDB driver to
disable OLEDB module.

Change-Id: I2a9dc5b1ae8ece45c85b3992026c7f389fd4d135
Signed-off-by: Kiran Gunda <kgunda@codeaurora.org>
This commit is contained in:
Kiran Gunda 2017-03-24 12:29:39 +05:30
parent 7f0d77b390
commit 91a6162f19
3 changed files with 52 additions and 4 deletions

View file

@ -151,6 +151,10 @@ LAB subnode optional properties:
any value in the allowed limit. any value in the allowed limit.
- qcom,notify-lab-vreg-ok-sts: A boolean property which upon set will - qcom,notify-lab-vreg-ok-sts: A boolean property which upon set will
poll and notify the lab_vreg_ok status. poll and notify the lab_vreg_ok status.
- qcom,qpnp-lab-sc-wait-time-ms: This property is used to specify the time
(in ms) to poll for the short circuit
detection. If not specified the default time
is 5 sec.
Following properties are available only for PM660A: Following properties are available only for PM660A:

View file

@ -559,6 +559,7 @@ struct lab_regulator {
int step_size; int step_size;
int slew_rate; int slew_rate;
int soft_start; int soft_start;
int sc_wait_time_ms;
int vreg_enabled; int vreg_enabled;
}; };
@ -608,6 +609,8 @@ struct qpnp_labibb {
bool skip_2nd_swire_cmd; bool skip_2nd_swire_cmd;
bool pfm_enable; bool pfm_enable;
bool notify_lab_vreg_ok_sts; bool notify_lab_vreg_ok_sts;
bool detect_lab_sc;
bool sc_detected;
u32 swire_2nd_cmd_delay; u32 swire_2nd_cmd_delay;
u32 swire_ibb_ps_enable_delay; u32 swire_ibb_ps_enable_delay;
}; };
@ -2138,8 +2141,10 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work)
u8 val; u8 val;
struct qpnp_labibb *labibb = container_of(work, struct qpnp_labibb, struct qpnp_labibb *labibb = container_of(work, struct qpnp_labibb,
lab_vreg_ok_work); lab_vreg_ok_work);
if (labibb->lab_vreg.sc_wait_time_ms != -EINVAL)
retries = labibb->lab_vreg.sc_wait_time_ms / 5;
while (retries--) { while (retries) {
rc = qpnp_labibb_read(labibb, labibb->lab_base + rc = qpnp_labibb_read(labibb, labibb->lab_base +
REG_LAB_STATUS1, &val, 1); REG_LAB_STATUS1, &val, 1);
if (rc < 0) { if (rc < 0) {
@ -2155,10 +2160,30 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work)
} }
usleep_range(dly, dly + 100); usleep_range(dly, dly + 100);
retries--;
} }
if (!retries) if (!retries) {
pr_err("LAB_VREG_OK not set, failed to notify\n"); if (labibb->detect_lab_sc) {
pr_crit("short circuit detected on LAB rail.. disabling the LAB/IBB/OLEDB modules\n");
/* Disable LAB module */
val = 0;
rc = qpnp_labibb_write(labibb, labibb->lab_base +
REG_LAB_MODULE_RDY, &val, 1);
if (rc < 0) {
pr_err("write register %x failed rc = %d\n",
REG_LAB_MODULE_RDY, rc);
return;
}
raw_notifier_call_chain(&labibb_notifier,
LAB_VREG_NOT_OK, NULL);
labibb->sc_detected = true;
labibb->lab_vreg.vreg_enabled = 0;
labibb->ibb_vreg.vreg_enabled = 0;
} else {
pr_err("LAB_VREG_OK not set, failed to notify\n");
}
}
} }
static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb) static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb)
@ -2323,6 +2348,11 @@ static int qpnp_lab_regulator_enable(struct regulator_dev *rdev)
struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); struct qpnp_labibb *labibb = rdev_get_drvdata(rdev);
if (labibb->sc_detected) {
pr_info("Short circuit detected: disabled LAB/IBB rails\n");
return 0;
}
if (labibb->skip_2nd_swire_cmd) { if (labibb->skip_2nd_swire_cmd) {
rc = qpnp_ibb_ps_config(labibb, false); rc = qpnp_ibb_ps_config(labibb, false);
if (rc < 0) { if (rc < 0) {
@ -2363,7 +2393,7 @@ static int qpnp_lab_regulator_enable(struct regulator_dev *rdev)
labibb->lab_vreg.vreg_enabled = 1; labibb->lab_vreg.vreg_enabled = 1;
} }
if (labibb->notify_lab_vreg_ok_sts) if (labibb->notify_lab_vreg_ok_sts || labibb->detect_lab_sc)
schedule_work(&labibb->lab_vreg_ok_work); schedule_work(&labibb->lab_vreg_ok_work);
return 0; return 0;
@ -2621,6 +2651,12 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb,
labibb->notify_lab_vreg_ok_sts = of_property_read_bool(of_node, labibb->notify_lab_vreg_ok_sts = of_property_read_bool(of_node,
"qcom,notify-lab-vreg-ok-sts"); "qcom,notify-lab-vreg-ok-sts");
labibb->lab_vreg.sc_wait_time_ms = -EINVAL;
if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE &&
labibb->detect_lab_sc)
of_property_read_u32(of_node, "qcom,qpnp-lab-sc-wait-time-ms",
&labibb->lab_vreg.sc_wait_time_ms);
rc = of_property_read_u32(of_node, "qcom,qpnp-lab-soft-start", rc = of_property_read_u32(of_node, "qcom,qpnp-lab-soft-start",
&(labibb->lab_vreg.soft_start)); &(labibb->lab_vreg.soft_start));
if (!rc) { if (!rc) {
@ -3255,6 +3291,11 @@ static int qpnp_ibb_regulator_enable(struct regulator_dev *rdev)
u8 val; u8 val;
struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); struct qpnp_labibb *labibb = rdev_get_drvdata(rdev);
if (labibb->sc_detected) {
pr_info("Short circuit detected: disabled LAB/IBB rails\n");
return 0;
}
if (!labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) { if (!labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) {
if (!labibb->standalone) if (!labibb->standalone)
@ -3731,6 +3772,8 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) { if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
labibb->mode = QPNP_LABIBB_AMOLED_MODE; labibb->mode = QPNP_LABIBB_AMOLED_MODE;
/* Enable polling for LAB short circuit detection for PM660A */
labibb->detect_lab_sc = true;
} else { } else {
rc = of_property_read_string(labibb->dev->of_node, rc = of_property_read_string(labibb->dev->of_node,
"qcom,qpnp-labibb-mode", &mode_name); "qcom,qpnp-labibb-mode", &mode_name);

View file

@ -15,6 +15,7 @@
enum labibb_notify_event { enum labibb_notify_event {
LAB_VREG_OK = 1, LAB_VREG_OK = 1,
LAB_VREG_NOT_OK,
}; };
int qpnp_labibb_notifier_register(struct notifier_block *nb); int qpnp_labibb_notifier_register(struct notifier_block *nb);