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>
This commit is contained in:
Fenglin Wu 2017-03-22 23:37:22 +08:00 committed by Kiran Gunda
parent 60be71604a
commit c21a36f4bd
2 changed files with 322 additions and 72 deletions

View file

@ -86,11 +86,11 @@ LAB subnode required properties:
40 and 50.
- interrupts: Specify the interrupts as per the interrupt
encoding.
Currently "lab-vreg-ok" is required for
LCD mode in pmi8998. For AMOLED mode,
"lab-vreg-ok" is required only when SWIRE
control is enabled and skipping 2nd SWIRE
pulse is required in pmi8952/8996.
Currently "lab-vreg-ok" is required and "lab-sc_err"
is optional for LCD mode in pmi8998.
For AMOLED mode, "lab-vreg-ok" is required
only when SWIRE control is enabled and skipping
2nd SWIRE pulse is required in pmi8952/8996.
- interrupt-names: Interrupt names to match up 1-to-1 with
the interrupts specified in 'interrupts'
property.
@ -211,6 +211,14 @@ IBB subnode required properties:
IBB subnode optional properties:
- interrupts: Specify the interrupts as per the interrupt
encoding.
Currently "ibb-sc-err" could be used for LCD mode
in pmi8998 to detect the short circuit fault.
- interrupt-names: Interrupt names to match up 1-to-1 with
the interrupts specified in 'interrupts'
property.
- qcom,qpnp-ibb-discharge-resistor: The discharge resistor in Kilo Ohms which
controls the soft start time. Supported values
are 300, 64, 32 and 16.

View file

@ -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);
rc = qpnp_lab_enable_standalone(labibb);
if (rc) {
pr_err("enable lab standalone failed, rc=%d\n", 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);
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,