summaryrefslogtreecommitdiff
path: root/drivers/regulator
diff options
context:
space:
mode:
authorFenglin Wu <fenglinw@codeaurora.org>2017-03-22 23:37:22 +0800
committerKiran Gunda <kgunda@codeaurora.org>2017-05-23 15:15:50 +0530
commitc21a36f4bdc3d7fc48a58640efc234fa053a50ee (patch)
tree11b13df24c9b3d5b080c1c0fb3cf4c47cbc05708 /drivers/regulator
parent60be71604a84d2e047215cb702d6324379a353bb (diff)
regulator: qpnp-labibb-regulator: Restart LAB/IBB after SC fault
PBS will be triggered in PMIC hardware to disable LAB/IBB regulators when a SC(short circuit) error is happened. The regulators won't be restart in hardware and they will be kept disabled always after that. Restart LAB/IBB regulator in the software if SC IRQ is detected, but stop doing this if the SC IRQ had fired frequently. CRs-Fixed: 2002373 Change-Id: I5db2b70999d043395e070bc9d61015477455cce9 Signed-off-by: Fenglin Wu <fenglinw@codeaurora.org>
Diffstat (limited to 'drivers/regulator')
-rw-r--r--drivers/regulator/qpnp-labibb-regulator.c376
1 files changed, 309 insertions, 67 deletions
diff --git a/drivers/regulator/qpnp-labibb-regulator.c b/drivers/regulator/qpnp-labibb-regulator.c
index 858ddcc228df..72c697bdcd29 100644
--- a/drivers/regulator/qpnp-labibb-regulator.c
+++ b/drivers/regulator/qpnp-labibb-regulator.c
@@ -17,6 +17,7 @@
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
+#include <linux/ktime.h>
#include <linux/regmap.h>
#include <linux/module.h>
#include <linux/notifier.h>
@@ -37,6 +38,7 @@
#define REG_REVISION_2 0x01
#define REG_PERPH_TYPE 0x04
+#define REG_INT_RT_STS 0x10
#define QPNP_LAB_TYPE 0x24
#define QPNP_IBB_TYPE 0x20
@@ -76,8 +78,8 @@
/* LAB register bits definitions */
/* REG_LAB_STATUS1 */
-#define LAB_STATUS1_VREG_OK_MASK BIT(7)
-#define LAB_STATUS1_VREG_OK BIT(7)
+#define LAB_STATUS1_VREG_OK_BIT BIT(7)
+#define LAB_STATUS1_SC_DETECT_BIT BIT(6)
/* REG_LAB_SWIRE_PGM_CTL */
#define LAB_EN_SWIRE_PGM_VOUT BIT(7)
@@ -184,8 +186,8 @@
/* IBB register bits definition */
/* REG_IBB_STATUS1 */
-#define IBB_STATUS1_VREG_OK_MASK BIT(7)
-#define IBB_STATUS1_VREG_OK BIT(7)
+#define IBB_STATUS1_VREG_OK_BIT BIT(7)
+#define IBB_STATUS1_SC_DETECT_BIT BIT(6)
/* REG_IBB_VOLTAGE */
#define IBB_VOLTAGE_OVERRIDE_EN BIT(7)
@@ -553,6 +555,8 @@ struct lab_regulator {
struct mutex lab_mutex;
int lab_vreg_ok_irq;
+ int lab_sc_irq;
+
int curr_volt;
int min_volt;
@@ -569,6 +573,8 @@ struct ibb_regulator {
struct regulator_dev *rdev;
struct mutex ibb_mutex;
+ int ibb_sc_irq;
+
int curr_volt;
int min_volt;
@@ -599,6 +605,9 @@ struct qpnp_labibb {
struct mutex bus_mutex;
enum qpnp_labibb_mode mode;
struct work_struct lab_vreg_ok_work;
+ struct delayed_work sc_err_recovery_work;
+ struct hrtimer sc_err_check_timer;
+ int sc_err_count;
bool standalone;
bool ttw_en;
bool in_ttw_mode;
@@ -2153,7 +2162,7 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work)
return;
}
- if (val & LAB_STATUS1_VREG_OK) {
+ if (val & LAB_STATUS1_VREG_OK_BIT) {
raw_notifier_call_chain(&labibb_notifier,
LAB_VREG_OK, NULL);
break;
@@ -2186,6 +2195,74 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work)
}
}
+static int qpnp_lab_enable_standalone(struct qpnp_labibb *labibb)
+{
+ int rc;
+ u8 val;
+
+ val = LAB_ENABLE_CTL_EN;
+ rc = qpnp_labibb_write(labibb,
+ labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1);
+ if (rc < 0) {
+ pr_err("Write register %x failed rc = %d\n",
+ REG_LAB_ENABLE_CTL, rc);
+ return rc;
+ }
+
+ udelay(labibb->lab_vreg.soft_start);
+
+ rc = qpnp_labibb_read(labibb, labibb->lab_base +
+ REG_LAB_STATUS1, &val, 1);
+ if (rc < 0) {
+ pr_err("Read register %x failed rc = %d\n",
+ REG_LAB_STATUS1, rc);
+ return rc;
+ }
+
+ if (!(val & LAB_STATUS1_VREG_OK_BIT)) {
+ pr_err("Can't enable LAB standalone\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int qpnp_ibb_enable_standalone(struct qpnp_labibb *labibb)
+{
+ int rc, delay, retries = 10;
+ u8 val;
+
+ rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN);
+ if (rc < 0) {
+ pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc);
+ return rc;
+ }
+
+ delay = labibb->ibb_vreg.soft_start;
+ while (retries--) {
+ /* Wait for a small period before reading IBB_STATUS1 */
+ usleep_range(delay, delay + 100);
+
+ rc = qpnp_labibb_read(labibb, labibb->ibb_base +
+ REG_IBB_STATUS1, &val, 1);
+ if (rc < 0) {
+ pr_err("Read register %x failed rc = %d\n",
+ REG_IBB_STATUS1, rc);
+ return rc;
+ }
+
+ if (val & IBB_STATUS1_VREG_OK_BIT)
+ break;
+ }
+
+ if (!(val & IBB_STATUS1_VREG_OK_BIT)) {
+ pr_err("Can't enable IBB standalone\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb)
{
int rc;
@@ -2227,7 +2304,7 @@ static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb)
labibb->lab_vreg.soft_start, labibb->ibb_vreg.soft_start,
labibb->ibb_vreg.pwrup_dly, dly);
- if (!(val & LAB_STATUS1_VREG_OK)) {
+ if (!(val & LAB_STATUS1_VREG_OK_BIT)) {
pr_err("failed for LAB %x\n", val);
goto err_out;
}
@@ -2244,7 +2321,7 @@ static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb)
goto err_out;
}
- if (val & IBB_STATUS1_VREG_OK) {
+ if (val & IBB_STATUS1_VREG_OK_BIT) {
enabled = true;
break;
}
@@ -2315,7 +2392,7 @@ static int qpnp_labibb_regulator_disable(struct qpnp_labibb *labibb)
return rc;
}
- if (!(val & IBB_STATUS1_VREG_OK)) {
+ if (!(val & IBB_STATUS1_VREG_OK_BIT)) {
disabled = true;
break;
}
@@ -2344,8 +2421,6 @@ static int qpnp_labibb_regulator_disable(struct qpnp_labibb *labibb)
static int qpnp_lab_regulator_enable(struct regulator_dev *rdev)
{
int rc;
- u8 val;
-
struct qpnp_labibb *labibb = rdev_get_drvdata(rdev);
if (labibb->sc_detected) {
@@ -2362,34 +2437,14 @@ static int qpnp_lab_regulator_enable(struct regulator_dev *rdev)
}
if (!labibb->lab_vreg.vreg_enabled && !labibb->swire_control) {
-
if (!labibb->standalone)
return qpnp_labibb_regulator_enable(labibb);
- val = LAB_ENABLE_CTL_EN;
- rc = qpnp_labibb_write(labibb,
- labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1);
- if (rc < 0) {
- pr_err("qpnp_lab_regulator_enable write register %x failed rc = %d\n",
- REG_LAB_ENABLE_CTL, rc);
- return rc;
- }
-
- udelay(labibb->lab_vreg.soft_start);
-
- rc = qpnp_labibb_read(labibb, labibb->lab_base +
- REG_LAB_STATUS1, &val, 1);
- if (rc < 0) {
- pr_err("qpnp_lab_regulator_enable read register %x failed rc = %d\n",
- REG_LAB_STATUS1, rc);
+ rc = qpnp_lab_enable_standalone(labibb);
+ if (rc) {
+ pr_err("enable lab standalone failed, rc=%d\n", rc);
return rc;
}
-
- if ((val & LAB_STATUS1_VREG_OK_MASK) != LAB_STATUS1_VREG_OK) {
- pr_err("qpnp_lab_regulator_enable failed\n");
- return -EINVAL;
- }
-
labibb->lab_vreg.vreg_enabled = 1;
}
@@ -2434,6 +2489,163 @@ static int qpnp_lab_regulator_is_enabled(struct regulator_dev *rdev)
return labibb->lab_vreg.vreg_enabled;
}
+static int qpnp_labibb_force_enable(struct qpnp_labibb *labibb)
+{
+ int rc;
+
+ if (labibb->skip_2nd_swire_cmd) {
+ rc = qpnp_ibb_ps_config(labibb, false);
+ if (rc < 0) {
+ pr_err("Failed to disable IBB PS rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ if (!labibb->swire_control) {
+ if (!labibb->standalone)
+ return qpnp_labibb_regulator_enable(labibb);
+
+ rc = qpnp_ibb_enable_standalone(labibb);
+ if (rc < 0) {
+ pr_err("enable ibb standalone failed, rc=%d\n", rc);
+ return rc;
+ }
+ labibb->ibb_vreg.vreg_enabled = 1;
+
+ rc = qpnp_lab_enable_standalone(labibb);
+ if (rc < 0) {
+ pr_err("enable lab standalone failed, rc=%d\n", rc);
+ return rc;
+ }
+ labibb->lab_vreg.vreg_enabled = 1;
+ }
+
+ return 0;
+}
+
+#define SC_ERR_RECOVERY_DELAY_MS 250
+#define SC_ERR_COUNT_INTERVAL_SEC 1
+#define POLLING_SCP_DONE_COUNT 2
+#define POLLING_SCP_DONE_INTERVAL_MS 5
+static irqreturn_t labibb_sc_err_handler(int irq, void *_labibb)
+{
+ int rc;
+ u16 reg;
+ u8 sc_err_mask, val;
+ char *str;
+ struct qpnp_labibb *labibb = (struct qpnp_labibb *)_labibb;
+ bool in_sc_err, lab_en, ibb_en, scp_done = false;
+ int count;
+
+ if (irq == labibb->lab_vreg.lab_sc_irq) {
+ reg = labibb->lab_base + REG_LAB_STATUS1;
+ sc_err_mask = LAB_STATUS1_SC_DETECT_BIT;
+ str = "LAB";
+ } else if (irq == labibb->ibb_vreg.ibb_sc_irq) {
+ reg = labibb->ibb_base + REG_IBB_STATUS1;
+ sc_err_mask = IBB_STATUS1_SC_DETECT_BIT;
+ str = "IBB";
+ } else {
+ return IRQ_HANDLED;
+ }
+
+ rc = qpnp_labibb_read(labibb, reg, &val, 1);
+ if (rc < 0) {
+ pr_err("Read 0x%x failed, rc=%d\n", reg, rc);
+ return IRQ_HANDLED;
+ }
+ pr_debug("%s SC error triggered! %s_STATUS1 = %d\n", str, str, val);
+
+ in_sc_err = !!(val & sc_err_mask);
+
+ /*
+ * The SC fault would trigger PBS to disable regulators
+ * for protection. This would cause the SC_DETECT status being
+ * cleared so that it's not able to get the SC fault status.
+ * Check if LAB/IBB regulators are enabled in the driver but
+ * disabled in hardware, this means a SC fault had happened
+ * and SCP handling is completed by PBS.
+ */
+ if (!in_sc_err) {
+ count = POLLING_SCP_DONE_COUNT;
+ do {
+ reg = labibb->lab_base + REG_LAB_ENABLE_CTL;
+ rc = qpnp_labibb_read(labibb, reg, &val, 1);
+ if (rc < 0) {
+ pr_err("Read 0x%x failed, rc=%d\n", reg, rc);
+ return IRQ_HANDLED;
+ }
+ lab_en = !!(val & LAB_ENABLE_CTL_EN);
+
+ reg = labibb->ibb_base + REG_IBB_ENABLE_CTL;
+ rc = qpnp_labibb_read(labibb, reg, &val, 1);
+ if (rc < 0) {
+ pr_err("Read 0x%x failed, rc=%d\n", reg, rc);
+ return IRQ_HANDLED;
+ }
+ ibb_en = !!(val & IBB_ENABLE_CTL_MODULE_EN);
+ if (lab_en || ibb_en)
+ msleep(POLLING_SCP_DONE_INTERVAL_MS);
+ else
+ break;
+ } while ((lab_en || ibb_en) && count--);
+
+ if (labibb->lab_vreg.vreg_enabled
+ && labibb->ibb_vreg.vreg_enabled
+ && !lab_en && !ibb_en) {
+ pr_debug("LAB/IBB has been disabled by SCP\n");
+ scp_done = true;
+ }
+ }
+
+ if (in_sc_err || scp_done) {
+ if (hrtimer_active(&labibb->sc_err_check_timer) ||
+ hrtimer_callback_running(&labibb->sc_err_check_timer)) {
+ labibb->sc_err_count++;
+ } else {
+ labibb->sc_err_count = 1;
+ hrtimer_start(&labibb->sc_err_check_timer,
+ ktime_set(SC_ERR_COUNT_INTERVAL_SEC, 0),
+ HRTIMER_MODE_REL);
+ }
+ schedule_delayed_work(&labibb->sc_err_recovery_work,
+ msecs_to_jiffies(SC_ERR_RECOVERY_DELAY_MS));
+ }
+
+ return IRQ_HANDLED;
+}
+
+#define SC_FAULT_COUNT_MAX 4
+static enum hrtimer_restart labibb_check_sc_err_count(struct hrtimer *timer)
+{
+ struct qpnp_labibb *labibb = container_of(timer,
+ struct qpnp_labibb, sc_err_check_timer);
+ /*
+ * if SC fault triggers more than 4 times in 1 second,
+ * then disable the IRQs and leave as it.
+ */
+ if (labibb->sc_err_count > SC_FAULT_COUNT_MAX) {
+ disable_irq(labibb->lab_vreg.lab_sc_irq);
+ disable_irq(labibb->ibb_vreg.ibb_sc_irq);
+ }
+
+ return HRTIMER_NORESTART;
+}
+
+static void labibb_sc_err_recovery_work(struct work_struct *work)
+{
+ struct qpnp_labibb *labibb = container_of(work, struct qpnp_labibb,
+ sc_err_recovery_work.work);
+ int rc;
+
+ labibb->ibb_vreg.vreg_enabled = 0;
+ labibb->lab_vreg.vreg_enabled = 0;
+ rc = qpnp_labibb_force_enable(labibb);
+ if (rc < 0)
+ pr_err("force enable labibb failed, rc=%d\n", rc);
+
+}
+
static int qpnp_lab_regulator_set_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV, unsigned int *selector)
{
@@ -2495,7 +2707,7 @@ static int qpnp_skip_swire_command(struct qpnp_labibb *labibb)
pr_err("Failed to read ibb_status1 reg rc=%d\n", rc);
return rc;
}
- if ((reg & IBB_STATUS1_VREG_OK_MASK) == IBB_STATUS1_VREG_OK)
+ if (reg & IBB_STATUS1_VREG_OK_BIT)
break;
/* poll delay */
@@ -2829,6 +3041,18 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb,
}
}
+ if (labibb->lab_vreg.lab_sc_irq != -EINVAL) {
+ rc = devm_request_threaded_irq(labibb->dev,
+ labibb->lab_vreg.lab_sc_irq, NULL,
+ labibb_sc_err_handler,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "lab-sc-err", labibb);
+ if (rc) {
+ pr_err("Failed to register 'lab-sc-err' irq rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_MODULE_RDY,
&val, 1);
if (rc < 0) {
@@ -3287,8 +3511,7 @@ static int qpnp_ibb_dt_init(struct qpnp_labibb *labibb,
static int qpnp_ibb_regulator_enable(struct regulator_dev *rdev)
{
- int rc, delay, retries = 10;
- u8 val;
+ int rc = 0;
struct qpnp_labibb *labibb = rdev_get_drvdata(rdev);
if (labibb->sc_detected) {
@@ -3297,40 +3520,17 @@ static int qpnp_ibb_regulator_enable(struct regulator_dev *rdev)
}
if (!labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) {
-
if (!labibb->standalone)
return qpnp_labibb_regulator_enable(labibb);
- rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN);
+ rc = qpnp_ibb_enable_standalone(labibb);
if (rc < 0) {
- pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc);
+ pr_err("enable ibb standalone failed, rc=%d\n", rc);
return rc;
}
-
- delay = labibb->ibb_vreg.soft_start;
- while (retries--) {
- /* Wait for a small period before reading IBB_STATUS1 */
- usleep_range(delay, delay + 100);
-
- rc = qpnp_labibb_read(labibb, labibb->ibb_base +
- REG_IBB_STATUS1, &val, 1);
- if (rc < 0) {
- pr_err("qpnp_ibb_regulator_enable read register %x failed rc = %d\n",
- REG_IBB_STATUS1, rc);
- return rc;
- }
-
- if (val & IBB_STATUS1_VREG_OK)
- break;
- }
-
- if (!(val & IBB_STATUS1_VREG_OK)) {
- pr_err("qpnp_ibb_regulator_enable failed\n");
- return -EINVAL;
- }
-
labibb->ibb_vreg.vreg_enabled = 1;
}
+
return 0;
}
@@ -3379,7 +3579,6 @@ static int qpnp_ibb_regulator_set_voltage(struct regulator_dev *rdev,
return rc;
}
-
static int qpnp_ibb_regulator_get_voltage(struct regulator_dev *rdev)
{
struct qpnp_labibb *labibb = rdev_get_drvdata(rdev);
@@ -3601,6 +3800,19 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb,
labibb->ibb_vreg.pwrdn_dly = 0;
}
+ if (labibb->ibb_vreg.ibb_sc_irq != -EINVAL) {
+ rc = devm_request_threaded_irq(labibb->dev,
+ labibb->ibb_vreg.ibb_sc_irq, NULL,
+ labibb_sc_err_handler,
+ IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+ "ibb-sc-err", labibb);
+ if (rc) {
+ pr_err("Failed to register 'ibb-sc-err' irq rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_MODULE_RDY,
&val, 1);
if (rc < 0) {
@@ -3674,15 +3886,39 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb,
static int qpnp_lab_register_irq(struct device_node *child,
struct qpnp_labibb *labibb)
{
+ int rc = 0;
+
if (is_lab_vreg_ok_irq_available(labibb)) {
- labibb->lab_vreg.lab_vreg_ok_irq =
- of_irq_get_byname(child, "lab-vreg-ok");
- if (labibb->lab_vreg.lab_vreg_ok_irq < 0) {
+ rc = of_irq_get_byname(child, "lab-vreg-ok");
+ if (rc < 0) {
pr_err("Invalid lab-vreg-ok irq\n");
- return -EINVAL;
+ return rc;
}
+ labibb->lab_vreg.lab_vreg_ok_irq = rc;
}
+ labibb->lab_vreg.lab_sc_irq = -EINVAL;
+ rc = of_irq_get_byname(child, "lab-sc-err");
+ if (rc < 0)
+ pr_debug("Unable to get lab-sc-err, rc = %d\n", rc);
+ else
+ labibb->lab_vreg.lab_sc_irq = rc;
+
+ return 0;
+}
+
+static int qpnp_ibb_register_irq(struct device_node *child,
+ struct qpnp_labibb *labibb)
+{
+ int rc;
+
+ labibb->ibb_vreg.ibb_sc_irq = -EINVAL;
+ rc = of_irq_get_byname(child, "ibb-sc-err");
+ if (rc < 0)
+ pr_debug("Unable to get ibb-sc-err, rc = %d\n", rc);
+ else
+ labibb->ibb_vreg.ibb_sc_irq = rc;
+
return 0;
}
@@ -3882,6 +4118,7 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
case QPNP_IBB_TYPE:
labibb->ibb_base = base;
labibb->ibb_dig_major = revision;
+ qpnp_ibb_register_irq(child, labibb);
rc = register_qpnp_ibb_regulator(labibb, child);
if (rc < 0)
goto fail_registration;
@@ -3905,6 +4142,11 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
}
INIT_WORK(&labibb->lab_vreg_ok_work, qpnp_lab_vreg_notifier_work);
+ INIT_DELAYED_WORK(&labibb->sc_err_recovery_work,
+ labibb_sc_err_recovery_work);
+ hrtimer_init(&labibb->sc_err_check_timer,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ labibb->sc_err_check_timer.function = labibb_check_sc_err_count;
dev_set_drvdata(&pdev->dev, labibb);
pr_info("LAB/IBB registered successfully, lab_vreg enable=%d ibb_vreg enable=%d swire_control=%d\n",
labibb->lab_vreg.vreg_enabled,