2020-06-22 20:41:10 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
// Copyright (c) 2020, The Linux Foundation. All rights reserved.
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/of_irq.h>
|
|
|
|
#include <linux/of.h>
|
|
|
|
#include <linux/of_device.h>
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <linux/regmap.h>
|
|
|
|
#include <linux/regulator/driver.h>
|
|
|
|
#include <linux/regulator/of_regulator.h>
|
|
|
|
|
|
|
|
#define REG_PERPH_TYPE 0x04
|
|
|
|
|
|
|
|
#define QCOM_LAB_TYPE 0x24
|
|
|
|
#define QCOM_IBB_TYPE 0x20
|
|
|
|
|
|
|
|
#define PMI8998_LAB_REG_BASE 0xde00
|
|
|
|
#define PMI8998_IBB_REG_BASE 0xdc00
|
regulator: qcom-labibb: Implement short-circuit and over-current IRQs
Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are
very important for regulators like LAB and IBB, which are designed to
provide from very small to relatively big amounts of current to the
device (normally, a display).
Now that this regulator supports both voltage setting and current
limiting in this driver, to me it looked like being somehow essential
to provide support for SCP and OCP, for two reasons:
1. SCP is a drastic measure to prevent damaging "more" hardware in
the worst situations, if any was damaged, preventing potentially
drastic issues;
2. OCP is a great way to protect the hardware that we're powering
through these regulators as if anything bad happens, the HW will
draw more current than expected: in this case, the OCP interrupt
will fire and the regulators will be immediately shut down,
preventing hardware damage in many cases.
Both interrupts were successfully tested in a "sort-of" controlled
manner, with the following methodology:
Short-Circuit Protection (SCP):
1. Set LAB/IBB to 4.6/-1.4V, current limit 200mA/50mA;
2. Connect a 10 KOhm resistor to LAB/IBB by poking the right traces
on a FxTec Pro1 smartphone for a very brief time (in short words,
"just a rapid touch with flying wires");
3. The Short-Circuit protection trips: IRQ raises, regulators get
cut. Recovery OK, test repeated without rebooting, OK.
Over-Current Protection (OCP):
1. Set LAB/IBB to the expected voltage to power up the display of
a Sony Xperia XZ Premium smartphone (Sharp LS055D1SX04), set
current limit to LAB 200mA, IBB 50mA (the values that this
display unit needs are 200/800mA);
2. Boot the kernel: OCP fires. Recovery never happens because
the selected current limit is too low, but that's expected.
Test OK.
3. Set LAB/IBB to the expected current limits for XZ Premium
(LAB 200mA, IBB 800mA), but lower than expected voltage,
specifically LAB 5.4V, IBB -5.6V (instead of 5.6, -5.8V);
4. Boot the kernel: OCP fires. Recovery never happens because
the selected voltage (still in the working range limits)
is producing a current draw of more than 200mA on LAB.
Test OK.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
Link: https://lore.kernel.org/r/20210119174421.226541-6-angelogioacchino.delregno@somainline.org
Signed-off-by: Mark Brown <broonie@kernel.org>
2021-01-20 01:44:19 +08:00
|
|
|
#define PMI8998_IBB_LAB_REG_OFFSET 0x200
|
2020-06-22 20:41:10 +08:00
|
|
|
|
|
|
|
#define REG_LABIBB_STATUS1 0x08
|
regulator: qcom-labibb: Implement short-circuit and over-current IRQs
Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are
very important for regulators like LAB and IBB, which are designed to
provide from very small to relatively big amounts of current to the
device (normally, a display).
Now that this regulator supports both voltage setting and current
limiting in this driver, to me it looked like being somehow essential
to provide support for SCP and OCP, for two reasons:
1. SCP is a drastic measure to prevent damaging "more" hardware in
the worst situations, if any was damaged, preventing potentially
drastic issues;
2. OCP is a great way to protect the hardware that we're powering
through these regulators as if anything bad happens, the HW will
draw more current than expected: in this case, the OCP interrupt
will fire and the regulators will be immediately shut down,
preventing hardware damage in many cases.
Both interrupts were successfully tested in a "sort-of" controlled
manner, with the following methodology:
Short-Circuit Protection (SCP):
1. Set LAB/IBB to 4.6/-1.4V, current limit 200mA/50mA;
2. Connect a 10 KOhm resistor to LAB/IBB by poking the right traces
on a FxTec Pro1 smartphone for a very brief time (in short words,
"just a rapid touch with flying wires");
3. The Short-Circuit protection trips: IRQ raises, regulators get
cut. Recovery OK, test repeated without rebooting, OK.
Over-Current Protection (OCP):
1. Set LAB/IBB to the expected voltage to power up the display of
a Sony Xperia XZ Premium smartphone (Sharp LS055D1SX04), set
current limit to LAB 200mA, IBB 50mA (the values that this
display unit needs are 200/800mA);
2. Boot the kernel: OCP fires. Recovery never happens because
the selected current limit is too low, but that's expected.
Test OK.
3. Set LAB/IBB to the expected current limits for XZ Premium
(LAB 200mA, IBB 800mA), but lower than expected voltage,
specifically LAB 5.4V, IBB -5.6V (instead of 5.6, -5.8V);
4. Boot the kernel: OCP fires. Recovery never happens because
the selected voltage (still in the working range limits)
is producing a current draw of more than 200mA on LAB.
Test OK.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
Link: https://lore.kernel.org/r/20210119174421.226541-6-angelogioacchino.delregno@somainline.org
Signed-off-by: Mark Brown <broonie@kernel.org>
2021-01-20 01:44:19 +08:00
|
|
|
#define LABIBB_STATUS1_SC_BIT BIT(6)
|
|
|
|
#define LABIBB_STATUS1_VREG_OK_BIT BIT(7)
|
|
|
|
|
|
|
|
#define REG_LABIBB_INT_SET_TYPE 0x11
|
|
|
|
#define REG_LABIBB_INT_POLARITY_HIGH 0x12
|
|
|
|
#define REG_LABIBB_INT_POLARITY_LOW 0x13
|
|
|
|
#define REG_LABIBB_INT_LATCHED_CLR 0x14
|
|
|
|
#define REG_LABIBB_INT_EN_SET 0x15
|
|
|
|
#define REG_LABIBB_INT_EN_CLR 0x16
|
|
|
|
#define LABIBB_INT_VREG_OK BIT(0)
|
|
|
|
#define LABIBB_INT_VREG_TYPE_LEVEL 0
|
2021-01-14 03:42:08 +08:00
|
|
|
|
|
|
|
#define REG_LABIBB_VOLTAGE 0x41
|
|
|
|
#define LABIBB_VOLTAGE_OVERRIDE_EN BIT(7)
|
|
|
|
#define LAB_VOLTAGE_SET_MASK GENMASK(3, 0)
|
|
|
|
#define IBB_VOLTAGE_SET_MASK GENMASK(5, 0)
|
|
|
|
|
2020-06-22 20:41:10 +08:00
|
|
|
#define REG_LABIBB_ENABLE_CTL 0x46
|
regulator: qcom-labibb: Implement short-circuit and over-current IRQs
Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are
very important for regulators like LAB and IBB, which are designed to
provide from very small to relatively big amounts of current to the
device (normally, a display).
Now that this regulator supports both voltage setting and current
limiting in this driver, to me it looked like being somehow essential
to provide support for SCP and OCP, for two reasons:
1. SCP is a drastic measure to prevent damaging "more" hardware in
the worst situations, if any was damaged, preventing potentially
drastic issues;
2. OCP is a great way to protect the hardware that we're powering
through these regulators as if anything bad happens, the HW will
draw more current than expected: in this case, the OCP interrupt
will fire and the regulators will be immediately shut down,
preventing hardware damage in many cases.
Both interrupts were successfully tested in a "sort-of" controlled
manner, with the following methodology:
Short-Circuit Protection (SCP):
1. Set LAB/IBB to 4.6/-1.4V, current limit 200mA/50mA;
2. Connect a 10 KOhm resistor to LAB/IBB by poking the right traces
on a FxTec Pro1 smartphone for a very brief time (in short words,
"just a rapid touch with flying wires");
3. The Short-Circuit protection trips: IRQ raises, regulators get
cut. Recovery OK, test repeated without rebooting, OK.
Over-Current Protection (OCP):
1. Set LAB/IBB to the expected voltage to power up the display of
a Sony Xperia XZ Premium smartphone (Sharp LS055D1SX04), set
current limit to LAB 200mA, IBB 50mA (the values that this
display unit needs are 200/800mA);
2. Boot the kernel: OCP fires. Recovery never happens because
the selected current limit is too low, but that's expected.
Test OK.
3. Set LAB/IBB to the expected current limits for XZ Premium
(LAB 200mA, IBB 800mA), but lower than expected voltage,
specifically LAB 5.4V, IBB -5.6V (instead of 5.6, -5.8V);
4. Boot the kernel: OCP fires. Recovery never happens because
the selected voltage (still in the working range limits)
is producing a current draw of more than 200mA on LAB.
Test OK.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
Link: https://lore.kernel.org/r/20210119174421.226541-6-angelogioacchino.delregno@somainline.org
Signed-off-by: Mark Brown <broonie@kernel.org>
2021-01-20 01:44:19 +08:00
|
|
|
#define LABIBB_CONTROL_ENABLE BIT(7)
|
2020-06-22 20:41:10 +08:00
|
|
|
|
2021-01-20 01:44:17 +08:00
|
|
|
#define REG_LABIBB_PD_CTL 0x47
|
|
|
|
#define LAB_PD_CTL_MASK GENMASK(1, 0)
|
|
|
|
#define IBB_PD_CTL_MASK (BIT(0) | BIT(7))
|
|
|
|
#define LAB_PD_CTL_STRONG_PULL BIT(0)
|
|
|
|
#define IBB_PD_CTL_HALF_STRENGTH BIT(0)
|
|
|
|
#define IBB_PD_CTL_EN BIT(7)
|
|
|
|
|
2021-01-20 01:44:16 +08:00
|
|
|
#define REG_LABIBB_CURRENT_LIMIT 0x4b
|
|
|
|
#define LAB_CURRENT_LIMIT_MASK GENMASK(2, 0)
|
|
|
|
#define IBB_CURRENT_LIMIT_MASK GENMASK(4, 0)
|
|
|
|
#define LAB_CURRENT_LIMIT_OVERRIDE_EN BIT(3)
|
|
|
|
#define LABIBB_CURRENT_LIMIT_EN BIT(7)
|
|
|
|
|
2021-01-20 01:44:17 +08:00
|
|
|
#define REG_IBB_PWRUP_PWRDN_CTL_1 0x58
|
|
|
|
#define IBB_CTL_1_DISCHARGE_EN BIT(2)
|
|
|
|
|
|
|
|
#define REG_LABIBB_SOFT_START_CTL 0x5f
|
2021-01-20 01:44:16 +08:00
|
|
|
#define REG_LABIBB_SEC_ACCESS 0xd0
|
|
|
|
#define LABIBB_SEC_UNLOCK_CODE 0xa5
|
|
|
|
|
2020-06-22 20:41:10 +08:00
|
|
|
#define LAB_ENABLE_CTL_MASK BIT(7)
|
|
|
|
#define IBB_ENABLE_CTL_MASK (BIT(7) | BIT(6))
|
|
|
|
|
|
|
|
#define LABIBB_OFF_ON_DELAY 1000
|
|
|
|
#define LAB_ENABLE_TIME (LABIBB_OFF_ON_DELAY * 2)
|
|
|
|
#define IBB_ENABLE_TIME (LABIBB_OFF_ON_DELAY * 10)
|
|
|
|
#define LABIBB_POLL_ENABLED_TIME 1000
|
regulator: qcom-labibb: Implement short-circuit and over-current IRQs
Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are
very important for regulators like LAB and IBB, which are designed to
provide from very small to relatively big amounts of current to the
device (normally, a display).
Now that this regulator supports both voltage setting and current
limiting in this driver, to me it looked like being somehow essential
to provide support for SCP and OCP, for two reasons:
1. SCP is a drastic measure to prevent damaging "more" hardware in
the worst situations, if any was damaged, preventing potentially
drastic issues;
2. OCP is a great way to protect the hardware that we're powering
through these regulators as if anything bad happens, the HW will
draw more current than expected: in this case, the OCP interrupt
will fire and the regulators will be immediately shut down,
preventing hardware damage in many cases.
Both interrupts were successfully tested in a "sort-of" controlled
manner, with the following methodology:
Short-Circuit Protection (SCP):
1. Set LAB/IBB to 4.6/-1.4V, current limit 200mA/50mA;
2. Connect a 10 KOhm resistor to LAB/IBB by poking the right traces
on a FxTec Pro1 smartphone for a very brief time (in short words,
"just a rapid touch with flying wires");
3. The Short-Circuit protection trips: IRQ raises, regulators get
cut. Recovery OK, test repeated without rebooting, OK.
Over-Current Protection (OCP):
1. Set LAB/IBB to the expected voltage to power up the display of
a Sony Xperia XZ Premium smartphone (Sharp LS055D1SX04), set
current limit to LAB 200mA, IBB 50mA (the values that this
display unit needs are 200/800mA);
2. Boot the kernel: OCP fires. Recovery never happens because
the selected current limit is too low, but that's expected.
Test OK.
3. Set LAB/IBB to the expected current limits for XZ Premium
(LAB 200mA, IBB 800mA), but lower than expected voltage,
specifically LAB 5.4V, IBB -5.6V (instead of 5.6, -5.8V);
4. Boot the kernel: OCP fires. Recovery never happens because
the selected voltage (still in the working range limits)
is producing a current draw of more than 200mA on LAB.
Test OK.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
Link: https://lore.kernel.org/r/20210119174421.226541-6-angelogioacchino.delregno@somainline.org
Signed-off-by: Mark Brown <broonie@kernel.org>
2021-01-20 01:44:19 +08:00
|
|
|
#define OCP_RECOVERY_INTERVAL_MS 500
|
|
|
|
#define SC_RECOVERY_INTERVAL_MS 250
|
|
|
|
#define LABIBB_MAX_OCP_COUNT 4
|
|
|
|
#define LABIBB_MAX_SC_COUNT 3
|
|
|
|
#define LABIBB_MAX_FATAL_COUNT 2
|
2020-06-22 20:41:10 +08:00
|
|
|
|
2021-01-20 01:44:16 +08:00
|
|
|
struct labibb_current_limits {
|
|
|
|
u32 uA_min;
|
|
|
|
u32 uA_step;
|
|
|
|
u8 ovr_val;
|
|
|
|
};
|
|
|
|
|
2020-06-22 20:41:10 +08:00
|
|
|
struct labibb_regulator {
|
|
|
|
struct regulator_desc desc;
|
|
|
|
struct device *dev;
|
|
|
|
struct regmap *regmap;
|
|
|
|
struct regulator_dev *rdev;
|
2021-01-20 01:44:16 +08:00
|
|
|
struct labibb_current_limits uA_limits;
|
regulator: qcom-labibb: Implement short-circuit and over-current IRQs
Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are
very important for regulators like LAB and IBB, which are designed to
provide from very small to relatively big amounts of current to the
device (normally, a display).
Now that this regulator supports both voltage setting and current
limiting in this driver, to me it looked like being somehow essential
to provide support for SCP and OCP, for two reasons:
1. SCP is a drastic measure to prevent damaging "more" hardware in
the worst situations, if any was damaged, preventing potentially
drastic issues;
2. OCP is a great way to protect the hardware that we're powering
through these regulators as if anything bad happens, the HW will
draw more current than expected: in this case, the OCP interrupt
will fire and the regulators will be immediately shut down,
preventing hardware damage in many cases.
Both interrupts were successfully tested in a "sort-of" controlled
manner, with the following methodology:
Short-Circuit Protection (SCP):
1. Set LAB/IBB to 4.6/-1.4V, current limit 200mA/50mA;
2. Connect a 10 KOhm resistor to LAB/IBB by poking the right traces
on a FxTec Pro1 smartphone for a very brief time (in short words,
"just a rapid touch with flying wires");
3. The Short-Circuit protection trips: IRQ raises, regulators get
cut. Recovery OK, test repeated without rebooting, OK.
Over-Current Protection (OCP):
1. Set LAB/IBB to the expected voltage to power up the display of
a Sony Xperia XZ Premium smartphone (Sharp LS055D1SX04), set
current limit to LAB 200mA, IBB 50mA (the values that this
display unit needs are 200/800mA);
2. Boot the kernel: OCP fires. Recovery never happens because
the selected current limit is too low, but that's expected.
Test OK.
3. Set LAB/IBB to the expected current limits for XZ Premium
(LAB 200mA, IBB 800mA), but lower than expected voltage,
specifically LAB 5.4V, IBB -5.6V (instead of 5.6, -5.8V);
4. Boot the kernel: OCP fires. Recovery never happens because
the selected voltage (still in the working range limits)
is producing a current draw of more than 200mA on LAB.
Test OK.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
Link: https://lore.kernel.org/r/20210119174421.226541-6-angelogioacchino.delregno@somainline.org
Signed-off-by: Mark Brown <broonie@kernel.org>
2021-01-20 01:44:19 +08:00
|
|
|
struct delayed_work ocp_recovery_work;
|
|
|
|
struct delayed_work sc_recovery_work;
|
2020-06-22 20:41:10 +08:00
|
|
|
u16 base;
|
|
|
|
u8 type;
|
2021-01-20 01:44:17 +08:00
|
|
|
u8 dischg_sel;
|
|
|
|
u8 soft_start_sel;
|
regulator: qcom-labibb: Implement short-circuit and over-current IRQs
Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are
very important for regulators like LAB and IBB, which are designed to
provide from very small to relatively big amounts of current to the
device (normally, a display).
Now that this regulator supports both voltage setting and current
limiting in this driver, to me it looked like being somehow essential
to provide support for SCP and OCP, for two reasons:
1. SCP is a drastic measure to prevent damaging "more" hardware in
the worst situations, if any was damaged, preventing potentially
drastic issues;
2. OCP is a great way to protect the hardware that we're powering
through these regulators as if anything bad happens, the HW will
draw more current than expected: in this case, the OCP interrupt
will fire and the regulators will be immediately shut down,
preventing hardware damage in many cases.
Both interrupts were successfully tested in a "sort-of" controlled
manner, with the following methodology:
Short-Circuit Protection (SCP):
1. Set LAB/IBB to 4.6/-1.4V, current limit 200mA/50mA;
2. Connect a 10 KOhm resistor to LAB/IBB by poking the right traces
on a FxTec Pro1 smartphone for a very brief time (in short words,
"just a rapid touch with flying wires");
3. The Short-Circuit protection trips: IRQ raises, regulators get
cut. Recovery OK, test repeated without rebooting, OK.
Over-Current Protection (OCP):
1. Set LAB/IBB to the expected voltage to power up the display of
a Sony Xperia XZ Premium smartphone (Sharp LS055D1SX04), set
current limit to LAB 200mA, IBB 50mA (the values that this
display unit needs are 200/800mA);
2. Boot the kernel: OCP fires. Recovery never happens because
the selected current limit is too low, but that's expected.
Test OK.
3. Set LAB/IBB to the expected current limits for XZ Premium
(LAB 200mA, IBB 800mA), but lower than expected voltage,
specifically LAB 5.4V, IBB -5.6V (instead of 5.6, -5.8V);
4. Boot the kernel: OCP fires. Recovery never happens because
the selected voltage (still in the working range limits)
is producing a current draw of more than 200mA on LAB.
Test OK.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
Link: https://lore.kernel.org/r/20210119174421.226541-6-angelogioacchino.delregno@somainline.org
Signed-off-by: Mark Brown <broonie@kernel.org>
2021-01-20 01:44:19 +08:00
|
|
|
int sc_irq;
|
|
|
|
int sc_count;
|
|
|
|
int ocp_irq;
|
|
|
|
int ocp_irq_count;
|
|
|
|
int fatal_count;
|
2020-06-22 20:41:10 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct labibb_regulator_data {
|
|
|
|
const char *name;
|
|
|
|
u8 type;
|
|
|
|
u16 base;
|
2020-10-01 00:26:02 +08:00
|
|
|
const struct regulator_desc *desc;
|
2020-06-22 20:41:10 +08:00
|
|
|
};
|
|
|
|
|
regulator: qcom-labibb: Implement short-circuit and over-current IRQs
Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are
very important for regulators like LAB and IBB, which are designed to
provide from very small to relatively big amounts of current to the
device (normally, a display).
Now that this regulator supports both voltage setting and current
limiting in this driver, to me it looked like being somehow essential
to provide support for SCP and OCP, for two reasons:
1. SCP is a drastic measure to prevent damaging "more" hardware in
the worst situations, if any was damaged, preventing potentially
drastic issues;
2. OCP is a great way to protect the hardware that we're powering
through these regulators as if anything bad happens, the HW will
draw more current than expected: in this case, the OCP interrupt
will fire and the regulators will be immediately shut down,
preventing hardware damage in many cases.
Both interrupts were successfully tested in a "sort-of" controlled
manner, with the following methodology:
Short-Circuit Protection (SCP):
1. Set LAB/IBB to 4.6/-1.4V, current limit 200mA/50mA;
2. Connect a 10 KOhm resistor to LAB/IBB by poking the right traces
on a FxTec Pro1 smartphone for a very brief time (in short words,
"just a rapid touch with flying wires");
3. The Short-Circuit protection trips: IRQ raises, regulators get
cut. Recovery OK, test repeated without rebooting, OK.
Over-Current Protection (OCP):
1. Set LAB/IBB to the expected voltage to power up the display of
a Sony Xperia XZ Premium smartphone (Sharp LS055D1SX04), set
current limit to LAB 200mA, IBB 50mA (the values that this
display unit needs are 200/800mA);
2. Boot the kernel: OCP fires. Recovery never happens because
the selected current limit is too low, but that's expected.
Test OK.
3. Set LAB/IBB to the expected current limits for XZ Premium
(LAB 200mA, IBB 800mA), but lower than expected voltage,
specifically LAB 5.4V, IBB -5.6V (instead of 5.6, -5.8V);
4. Boot the kernel: OCP fires. Recovery never happens because
the selected voltage (still in the working range limits)
is producing a current draw of more than 200mA on LAB.
Test OK.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
Link: https://lore.kernel.org/r/20210119174421.226541-6-angelogioacchino.delregno@somainline.org
Signed-off-by: Mark Brown <broonie@kernel.org>
2021-01-20 01:44:19 +08:00
|
|
|
static int qcom_labibb_ocp_hw_enable(struct regulator_dev *rdev)
|
|
|
|
{
|
|
|
|
struct labibb_regulator *vreg = rdev_get_drvdata(rdev);
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* Clear irq latch status to avoid spurious event */
|
|
|
|
ret = regmap_update_bits(rdev->regmap,
|
|
|
|
vreg->base + REG_LABIBB_INT_LATCHED_CLR,
|
|
|
|
LABIBB_INT_VREG_OK, 1);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Enable OCP HW interrupt */
|
|
|
|
return regmap_update_bits(rdev->regmap,
|
|
|
|
vreg->base + REG_LABIBB_INT_EN_SET,
|
|
|
|
LABIBB_INT_VREG_OK, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qcom_labibb_ocp_hw_disable(struct regulator_dev *rdev)
|
|
|
|
{
|
|
|
|
struct labibb_regulator *vreg = rdev_get_drvdata(rdev);
|
|
|
|
|
|
|
|
return regmap_update_bits(rdev->regmap,
|
|
|
|
vreg->base + REG_LABIBB_INT_EN_CLR,
|
|
|
|
LABIBB_INT_VREG_OK, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qcom_labibb_check_ocp_status - Check the Over-Current Protection status
|
|
|
|
* @vreg: Main driver structure
|
|
|
|
*
|
|
|
|
* This function checks the STATUS1 register for the VREG_OK bit: if it is
|
|
|
|
* set, then there is no Over-Current event.
|
|
|
|
*
|
|
|
|
* Returns: Zero if there is no over-current, 1 if in over-current or
|
|
|
|
* negative number for error
|
|
|
|
*/
|
|
|
|
static int qcom_labibb_check_ocp_status(struct labibb_regulator *vreg)
|
|
|
|
{
|
|
|
|
u32 cur_status;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = regmap_read(vreg->rdev->regmap, vreg->base + REG_LABIBB_STATUS1,
|
|
|
|
&cur_status);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return !(cur_status & LABIBB_STATUS1_VREG_OK_BIT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qcom_labibb_ocp_recovery_worker - Handle OCP event
|
|
|
|
* @work: OCP work structure
|
|
|
|
*
|
|
|
|
* This is the worker function to handle the Over Current Protection
|
|
|
|
* hardware event; This will check if the hardware is still
|
|
|
|
* signaling an over-current condition and will eventually stop
|
|
|
|
* the regulator if such condition is still signaled after
|
|
|
|
* LABIBB_MAX_OCP_COUNT times.
|
|
|
|
*
|
|
|
|
* If the driver that is consuming the regulator did not take action
|
|
|
|
* for the OCP condition, or the hardware did not stabilize, a cut
|
|
|
|
* of the LAB and IBB regulators will be forced (regulators will be
|
|
|
|
* disabled).
|
|
|
|
*
|
|
|
|
* As last, if the writes to shut down the LAB/IBB regulators fail
|
|
|
|
* for more than LABIBB_MAX_FATAL_COUNT, then a kernel panic will be
|
|
|
|
* triggered, as a last resort to protect the hardware from burning;
|
|
|
|
* this, however, is expected to never happen, but this is kept to
|
|
|
|
* try to further ensure that we protect the hardware at all costs.
|
|
|
|
*/
|
|
|
|
static void qcom_labibb_ocp_recovery_worker(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct labibb_regulator *vreg;
|
|
|
|
const struct regulator_ops *ops;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
vreg = container_of(work, struct labibb_regulator,
|
|
|
|
ocp_recovery_work.work);
|
|
|
|
ops = vreg->rdev->desc->ops;
|
|
|
|
|
|
|
|
if (vreg->ocp_irq_count >= LABIBB_MAX_OCP_COUNT) {
|
|
|
|
/*
|
|
|
|
* If we tried to disable the regulator multiple times but
|
|
|
|
* we kept failing, there's only one last hope to save our
|
|
|
|
* hardware from the death: raise a kernel bug, reboot and
|
|
|
|
* hope that the bootloader kindly saves us. This, though
|
|
|
|
* is done only as paranoid checking, because failing the
|
|
|
|
* regmap write to disable the vreg is almost impossible,
|
|
|
|
* since we got here after multiple regmap R/W.
|
|
|
|
*/
|
|
|
|
BUG_ON(vreg->fatal_count > LABIBB_MAX_FATAL_COUNT);
|
|
|
|
dev_err(&vreg->rdev->dev, "LABIBB: CRITICAL: Disabling regulator\n");
|
|
|
|
|
|
|
|
/* Disable the regulator immediately to avoid damage */
|
|
|
|
ret = ops->disable(vreg->rdev);
|
|
|
|
if (ret) {
|
|
|
|
vreg->fatal_count++;
|
|
|
|
goto reschedule;
|
|
|
|
}
|
|
|
|
enable_irq(vreg->ocp_irq);
|
|
|
|
vreg->fatal_count = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qcom_labibb_check_ocp_status(vreg);
|
|
|
|
if (ret != 0) {
|
|
|
|
vreg->ocp_irq_count++;
|
|
|
|
goto reschedule;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = qcom_labibb_ocp_hw_enable(vreg->rdev);
|
|
|
|
if (ret) {
|
|
|
|
/* We cannot trust it without OCP enabled. */
|
|
|
|
dev_err(vreg->dev, "Cannot enable OCP IRQ\n");
|
|
|
|
vreg->ocp_irq_count++;
|
|
|
|
goto reschedule;
|
|
|
|
}
|
|
|
|
|
|
|
|
enable_irq(vreg->ocp_irq);
|
|
|
|
/* Everything went fine: reset the OCP count! */
|
|
|
|
vreg->ocp_irq_count = 0;
|
|
|
|
return;
|
|
|
|
|
|
|
|
reschedule:
|
|
|
|
mod_delayed_work(system_wq, &vreg->ocp_recovery_work,
|
|
|
|
msecs_to_jiffies(OCP_RECOVERY_INTERVAL_MS));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qcom_labibb_ocp_isr - Interrupt routine for OverCurrent Protection
|
|
|
|
* @irq: Interrupt number
|
|
|
|
* @chip: Main driver structure
|
|
|
|
*
|
|
|
|
* Over Current Protection (OCP) will signal to the client driver
|
|
|
|
* that an over-current event has happened and then will schedule
|
|
|
|
* a recovery worker.
|
|
|
|
*
|
|
|
|
* Disabling and eventually re-enabling the regulator is expected
|
|
|
|
* to be done by the driver, as some hardware may be triggering an
|
|
|
|
* over-current condition only at first initialization or it may
|
|
|
|
* be expected only for a very brief amount of time, after which
|
|
|
|
* the attached hardware may be expected to stabilize its current
|
|
|
|
* draw.
|
|
|
|
*
|
|
|
|
* Returns: IRQ_HANDLED for success or IRQ_NONE for failure.
|
|
|
|
*/
|
|
|
|
static irqreturn_t qcom_labibb_ocp_isr(int irq, void *chip)
|
|
|
|
{
|
|
|
|
struct labibb_regulator *vreg = chip;
|
|
|
|
const struct regulator_ops *ops = vreg->rdev->desc->ops;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* If the regulator is not enabled, this is a fake event */
|
|
|
|
if (!ops->is_enabled(vreg->rdev))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* If we tried to recover for too many times it's not getting better */
|
|
|
|
if (vreg->ocp_irq_count > LABIBB_MAX_OCP_COUNT)
|
|
|
|
return IRQ_NONE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we (unlikely) can't read this register, to prevent hardware
|
|
|
|
* damage at all costs, we assume that the overcurrent event was
|
|
|
|
* real; Moreover, if the status register is not signaling OCP,
|
|
|
|
* it was a spurious event, so it's all ok.
|
|
|
|
*/
|
|
|
|
ret = qcom_labibb_check_ocp_status(vreg);
|
|
|
|
if (ret == 0) {
|
|
|
|
vreg->ocp_irq_count = 0;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
vreg->ocp_irq_count++;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Disable the interrupt temporarily, or it will fire continuously;
|
|
|
|
* we will re-enable it in the recovery worker function.
|
|
|
|
*/
|
2021-02-02 15:37:58 +08:00
|
|
|
disable_irq_nosync(irq);
|
regulator: qcom-labibb: Implement short-circuit and over-current IRQs
Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are
very important for regulators like LAB and IBB, which are designed to
provide from very small to relatively big amounts of current to the
device (normally, a display).
Now that this regulator supports both voltage setting and current
limiting in this driver, to me it looked like being somehow essential
to provide support for SCP and OCP, for two reasons:
1. SCP is a drastic measure to prevent damaging "more" hardware in
the worst situations, if any was damaged, preventing potentially
drastic issues;
2. OCP is a great way to protect the hardware that we're powering
through these regulators as if anything bad happens, the HW will
draw more current than expected: in this case, the OCP interrupt
will fire and the regulators will be immediately shut down,
preventing hardware damage in many cases.
Both interrupts were successfully tested in a "sort-of" controlled
manner, with the following methodology:
Short-Circuit Protection (SCP):
1. Set LAB/IBB to 4.6/-1.4V, current limit 200mA/50mA;
2. Connect a 10 KOhm resistor to LAB/IBB by poking the right traces
on a FxTec Pro1 smartphone for a very brief time (in short words,
"just a rapid touch with flying wires");
3. The Short-Circuit protection trips: IRQ raises, regulators get
cut. Recovery OK, test repeated without rebooting, OK.
Over-Current Protection (OCP):
1. Set LAB/IBB to the expected voltage to power up the display of
a Sony Xperia XZ Premium smartphone (Sharp LS055D1SX04), set
current limit to LAB 200mA, IBB 50mA (the values that this
display unit needs are 200/800mA);
2. Boot the kernel: OCP fires. Recovery never happens because
the selected current limit is too low, but that's expected.
Test OK.
3. Set LAB/IBB to the expected current limits for XZ Premium
(LAB 200mA, IBB 800mA), but lower than expected voltage,
specifically LAB 5.4V, IBB -5.6V (instead of 5.6, -5.8V);
4. Boot the kernel: OCP fires. Recovery never happens because
the selected voltage (still in the working range limits)
is producing a current draw of more than 200mA on LAB.
Test OK.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
Link: https://lore.kernel.org/r/20210119174421.226541-6-angelogioacchino.delregno@somainline.org
Signed-off-by: Mark Brown <broonie@kernel.org>
2021-01-20 01:44:19 +08:00
|
|
|
|
|
|
|
/* Warn the user for overcurrent */
|
|
|
|
dev_warn(vreg->dev, "Over-Current interrupt fired!\n");
|
|
|
|
|
|
|
|
/* Disable the interrupt to avoid hogging */
|
|
|
|
ret = qcom_labibb_ocp_hw_disable(vreg->rdev);
|
|
|
|
if (ret)
|
|
|
|
goto end;
|
|
|
|
|
|
|
|
/* Signal overcurrent event to drivers */
|
|
|
|
regulator_notifier_call_chain(vreg->rdev,
|
|
|
|
REGULATOR_EVENT_OVER_CURRENT, NULL);
|
|
|
|
|
|
|
|
end:
|
|
|
|
/* Schedule the recovery work */
|
|
|
|
schedule_delayed_work(&vreg->ocp_recovery_work,
|
|
|
|
msecs_to_jiffies(OCP_RECOVERY_INTERVAL_MS));
|
|
|
|
if (ret)
|
|
|
|
return IRQ_NONE;
|
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qcom_labibb_set_ocp(struct regulator_dev *rdev)
|
|
|
|
{
|
|
|
|
struct labibb_regulator *vreg = rdev_get_drvdata(rdev);
|
|
|
|
char *ocp_irq_name;
|
|
|
|
u32 irq_flags = IRQF_ONESHOT;
|
|
|
|
int irq_trig_low, ret;
|
|
|
|
|
|
|
|
/* If there is no OCP interrupt, there's nothing to set */
|
|
|
|
if (vreg->ocp_irq <= 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
ocp_irq_name = devm_kasprintf(vreg->dev, GFP_KERNEL, "%s-over-current",
|
|
|
|
vreg->desc.name);
|
|
|
|
if (!ocp_irq_name)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
/* IRQ polarities - LAB: trigger-low, IBB: trigger-high */
|
|
|
|
switch (vreg->type) {
|
|
|
|
case QCOM_LAB_TYPE:
|
|
|
|
irq_flags |= IRQF_TRIGGER_LOW;
|
|
|
|
irq_trig_low = 1;
|
|
|
|
break;
|
|
|
|
case QCOM_IBB_TYPE:
|
|
|
|
irq_flags |= IRQF_TRIGGER_HIGH;
|
|
|
|
irq_trig_low = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Activate OCP HW level interrupt */
|
|
|
|
ret = regmap_update_bits(rdev->regmap,
|
|
|
|
vreg->base + REG_LABIBB_INT_SET_TYPE,
|
|
|
|
LABIBB_INT_VREG_OK,
|
|
|
|
LABIBB_INT_VREG_TYPE_LEVEL);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Set OCP interrupt polarity */
|
|
|
|
ret = regmap_update_bits(rdev->regmap,
|
|
|
|
vreg->base + REG_LABIBB_INT_POLARITY_HIGH,
|
|
|
|
LABIBB_INT_VREG_OK, !irq_trig_low);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = regmap_update_bits(rdev->regmap,
|
|
|
|
vreg->base + REG_LABIBB_INT_POLARITY_LOW,
|
|
|
|
LABIBB_INT_VREG_OK, irq_trig_low);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = qcom_labibb_ocp_hw_enable(rdev);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return devm_request_threaded_irq(vreg->dev, vreg->ocp_irq, NULL,
|
|
|
|
qcom_labibb_ocp_isr, irq_flags,
|
|
|
|
ocp_irq_name, vreg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qcom_labibb_check_sc_status - Check the Short Circuit Protection status
|
|
|
|
* @vreg: Main driver structure
|
|
|
|
*
|
|
|
|
* This function checks the STATUS1 register on both LAB and IBB regulators
|
|
|
|
* for the ShortCircuit bit: if it is set on *any* of them, then we have
|
|
|
|
* experienced a short-circuit event.
|
|
|
|
*
|
|
|
|
* Returns: Zero if there is no short-circuit, 1 if in short-circuit or
|
|
|
|
* negative number for error
|
|
|
|
*/
|
|
|
|
static int qcom_labibb_check_sc_status(struct labibb_regulator *vreg)
|
|
|
|
{
|
|
|
|
u32 ibb_status, ibb_reg, lab_status, lab_reg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* We have to work on both regulators due to PBS... */
|
|
|
|
lab_reg = ibb_reg = vreg->base + REG_LABIBB_STATUS1;
|
|
|
|
if (vreg->type == QCOM_LAB_TYPE)
|
|
|
|
ibb_reg -= PMI8998_IBB_LAB_REG_OFFSET;
|
|
|
|
else
|
|
|
|
lab_reg += PMI8998_IBB_LAB_REG_OFFSET;
|
|
|
|
|
|
|
|
ret = regmap_read(vreg->rdev->regmap, lab_reg, &lab_status);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
ret = regmap_read(vreg->rdev->regmap, ibb_reg, &ibb_status);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
return !!(lab_status & LABIBB_STATUS1_SC_BIT) ||
|
|
|
|
!!(ibb_status & LABIBB_STATUS1_SC_BIT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qcom_labibb_sc_recovery_worker - Handle Short Circuit event
|
|
|
|
* @work: SC work structure
|
|
|
|
*
|
|
|
|
* This is the worker function to handle the Short Circuit Protection
|
|
|
|
* hardware event; This will check if the hardware is still
|
|
|
|
* signaling a short-circuit condition and will eventually never
|
|
|
|
* re-enable the regulator if such condition is still signaled after
|
|
|
|
* LABIBB_MAX_SC_COUNT times.
|
|
|
|
*
|
|
|
|
* If the driver that is consuming the regulator did not take action
|
|
|
|
* for the SC condition, or the hardware did not stabilize, this
|
|
|
|
* worker will stop rescheduling, leaving the regulators disabled
|
|
|
|
* as already done by the Portable Batch System (PBS).
|
|
|
|
*
|
|
|
|
* Returns: IRQ_HANDLED for success or IRQ_NONE for failure.
|
|
|
|
*/
|
|
|
|
static void qcom_labibb_sc_recovery_worker(struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct labibb_regulator *vreg;
|
|
|
|
const struct regulator_ops *ops;
|
|
|
|
u32 lab_reg, ibb_reg, lab_val, ibb_val, val;
|
|
|
|
bool pbs_cut = false;
|
|
|
|
int i, sc, ret;
|
|
|
|
|
|
|
|
vreg = container_of(work, struct labibb_regulator,
|
|
|
|
sc_recovery_work.work);
|
|
|
|
ops = vreg->rdev->desc->ops;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we tried to check the regulator status multiple times but we
|
|
|
|
* kept failing, then just bail out, as the Portable Batch System
|
|
|
|
* (PBS) will disable the vregs for us, preventing hardware damage.
|
|
|
|
*/
|
|
|
|
if (vreg->fatal_count > LABIBB_MAX_FATAL_COUNT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Too many short-circuit events. Throw in the towel. */
|
|
|
|
if (vreg->sc_count > LABIBB_MAX_SC_COUNT)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The Portable Batch System (PBS) automatically disables LAB
|
|
|
|
* and IBB when a short-circuit event is detected, so we have to
|
|
|
|
* check and work on both of them at the same time.
|
|
|
|
*/
|
|
|
|
lab_reg = ibb_reg = vreg->base + REG_LABIBB_ENABLE_CTL;
|
|
|
|
if (vreg->type == QCOM_LAB_TYPE)
|
|
|
|
ibb_reg -= PMI8998_IBB_LAB_REG_OFFSET;
|
|
|
|
else
|
|
|
|
lab_reg += PMI8998_IBB_LAB_REG_OFFSET;
|
|
|
|
|
|
|
|
sc = qcom_labibb_check_sc_status(vreg);
|
|
|
|
if (sc)
|
|
|
|
goto reschedule;
|
|
|
|
|
|
|
|
for (i = 0; i < LABIBB_MAX_SC_COUNT; i++) {
|
|
|
|
ret = regmap_read(vreg->regmap, lab_reg, &lab_val);
|
|
|
|
if (ret) {
|
|
|
|
vreg->fatal_count++;
|
|
|
|
goto reschedule;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = regmap_read(vreg->regmap, ibb_reg, &ibb_val);
|
|
|
|
if (ret) {
|
|
|
|
vreg->fatal_count++;
|
|
|
|
goto reschedule;
|
|
|
|
}
|
|
|
|
val = lab_val & ibb_val;
|
|
|
|
|
|
|
|
if (!(val & LABIBB_CONTROL_ENABLE)) {
|
|
|
|
pbs_cut = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
usleep_range(5000, 6000);
|
|
|
|
}
|
|
|
|
if (pbs_cut)
|
|
|
|
goto reschedule;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we have reached this point, we either have successfully
|
|
|
|
* recovered from the SC condition or we had a spurious SC IRQ,
|
|
|
|
* which means that we can re-enable the regulators, if they
|
|
|
|
* have ever been disabled by the PBS.
|
|
|
|
*/
|
|
|
|
ret = ops->enable(vreg->rdev);
|
|
|
|
if (ret)
|
|
|
|
goto reschedule;
|
|
|
|
|
|
|
|
/* Everything went fine: reset the OCP count! */
|
|
|
|
vreg->sc_count = 0;
|
|
|
|
enable_irq(vreg->sc_irq);
|
|
|
|
return;
|
|
|
|
|
|
|
|
reschedule:
|
|
|
|
/*
|
|
|
|
* Now that we have done basic handling of the short-circuit,
|
|
|
|
* reschedule this worker in the regular system workqueue, as
|
|
|
|
* taking action is not truly urgent anymore.
|
|
|
|
*/
|
|
|
|
vreg->sc_count++;
|
|
|
|
mod_delayed_work(system_wq, &vreg->sc_recovery_work,
|
|
|
|
msecs_to_jiffies(SC_RECOVERY_INTERVAL_MS));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* qcom_labibb_sc_isr - Interrupt routine for Short Circuit Protection
|
|
|
|
* @irq: Interrupt number
|
|
|
|
* @chip: Main driver structure
|
|
|
|
*
|
|
|
|
* Short Circuit Protection (SCP) will signal to the client driver
|
|
|
|
* that a regulation-out event has happened and then will schedule
|
|
|
|
* a recovery worker.
|
|
|
|
*
|
|
|
|
* The LAB and IBB regulators will be automatically disabled by the
|
|
|
|
* Portable Batch System (PBS) and they will be enabled again by
|
|
|
|
* the worker function if the hardware stops signaling the short
|
|
|
|
* circuit event.
|
|
|
|
*
|
|
|
|
* Returns: IRQ_HANDLED for success or IRQ_NONE for failure.
|
|
|
|
*/
|
|
|
|
static irqreturn_t qcom_labibb_sc_isr(int irq, void *chip)
|
|
|
|
{
|
|
|
|
struct labibb_regulator *vreg = chip;
|
|
|
|
|
|
|
|
if (vreg->sc_count > LABIBB_MAX_SC_COUNT)
|
|
|
|
return IRQ_NONE;
|
|
|
|
|
|
|
|
/* Warn the user for short circuit */
|
|
|
|
dev_warn(vreg->dev, "Short-Circuit interrupt fired!\n");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Disable the interrupt temporarily, or it will fire continuously;
|
|
|
|
* we will re-enable it in the recovery worker function.
|
|
|
|
*/
|
2021-02-02 15:37:58 +08:00
|
|
|
disable_irq_nosync(irq);
|
regulator: qcom-labibb: Implement short-circuit and over-current IRQs
Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are
very important for regulators like LAB and IBB, which are designed to
provide from very small to relatively big amounts of current to the
device (normally, a display).
Now that this regulator supports both voltage setting and current
limiting in this driver, to me it looked like being somehow essential
to provide support for SCP and OCP, for two reasons:
1. SCP is a drastic measure to prevent damaging "more" hardware in
the worst situations, if any was damaged, preventing potentially
drastic issues;
2. OCP is a great way to protect the hardware that we're powering
through these regulators as if anything bad happens, the HW will
draw more current than expected: in this case, the OCP interrupt
will fire and the regulators will be immediately shut down,
preventing hardware damage in many cases.
Both interrupts were successfully tested in a "sort-of" controlled
manner, with the following methodology:
Short-Circuit Protection (SCP):
1. Set LAB/IBB to 4.6/-1.4V, current limit 200mA/50mA;
2. Connect a 10 KOhm resistor to LAB/IBB by poking the right traces
on a FxTec Pro1 smartphone for a very brief time (in short words,
"just a rapid touch with flying wires");
3. The Short-Circuit protection trips: IRQ raises, regulators get
cut. Recovery OK, test repeated without rebooting, OK.
Over-Current Protection (OCP):
1. Set LAB/IBB to the expected voltage to power up the display of
a Sony Xperia XZ Premium smartphone (Sharp LS055D1SX04), set
current limit to LAB 200mA, IBB 50mA (the values that this
display unit needs are 200/800mA);
2. Boot the kernel: OCP fires. Recovery never happens because
the selected current limit is too low, but that's expected.
Test OK.
3. Set LAB/IBB to the expected current limits for XZ Premium
(LAB 200mA, IBB 800mA), but lower than expected voltage,
specifically LAB 5.4V, IBB -5.6V (instead of 5.6, -5.8V);
4. Boot the kernel: OCP fires. Recovery never happens because
the selected voltage (still in the working range limits)
is producing a current draw of more than 200mA on LAB.
Test OK.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
Link: https://lore.kernel.org/r/20210119174421.226541-6-angelogioacchino.delregno@somainline.org
Signed-off-by: Mark Brown <broonie@kernel.org>
2021-01-20 01:44:19 +08:00
|
|
|
|
|
|
|
/* Signal out of regulation event to drivers */
|
|
|
|
regulator_notifier_call_chain(vreg->rdev,
|
|
|
|
REGULATOR_EVENT_REGULATION_OUT, NULL);
|
|
|
|
|
|
|
|
/* Schedule the short-circuit handling as high-priority work */
|
|
|
|
mod_delayed_work(system_highpri_wq, &vreg->sc_recovery_work,
|
|
|
|
msecs_to_jiffies(SC_RECOVERY_INTERVAL_MS));
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-01-20 01:44:16 +08:00
|
|
|
static int qcom_labibb_set_current_limit(struct regulator_dev *rdev,
|
|
|
|
int min_uA, int max_uA)
|
|
|
|
{
|
|
|
|
struct labibb_regulator *vreg = rdev_get_drvdata(rdev);
|
|
|
|
struct regulator_desc *desc = &vreg->desc;
|
|
|
|
struct labibb_current_limits *lim = &vreg->uA_limits;
|
|
|
|
u32 mask, val;
|
|
|
|
int i, ret, sel = -1;
|
|
|
|
|
|
|
|
if (min_uA < lim->uA_min || max_uA < lim->uA_min)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
for (i = 0; i < desc->n_current_limits; i++) {
|
|
|
|
int uA_limit = (lim->uA_step * i) + lim->uA_min;
|
|
|
|
|
|
|
|
if (max_uA >= uA_limit && min_uA <= uA_limit)
|
|
|
|
sel = i;
|
|
|
|
}
|
|
|
|
if (sel < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* Current limit setting needs secure access */
|
|
|
|
ret = regmap_write(vreg->regmap, vreg->base + REG_LABIBB_SEC_ACCESS,
|
|
|
|
LABIBB_SEC_UNLOCK_CODE);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
mask = desc->csel_mask | lim->ovr_val;
|
|
|
|
mask |= LABIBB_CURRENT_LIMIT_EN;
|
|
|
|
val = (u32)sel | lim->ovr_val;
|
|
|
|
val |= LABIBB_CURRENT_LIMIT_EN;
|
|
|
|
|
|
|
|
return regmap_update_bits(vreg->regmap, desc->csel_reg, mask, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qcom_labibb_get_current_limit(struct regulator_dev *rdev)
|
|
|
|
{
|
|
|
|
struct labibb_regulator *vreg = rdev_get_drvdata(rdev);
|
|
|
|
struct regulator_desc *desc = &vreg->desc;
|
|
|
|
struct labibb_current_limits *lim = &vreg->uA_limits;
|
|
|
|
unsigned int cur_step;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = regmap_read(vreg->regmap, desc->csel_reg, &cur_step);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
cur_step &= desc->csel_mask;
|
|
|
|
|
|
|
|
return (cur_step * lim->uA_step) + lim->uA_min;
|
|
|
|
}
|
|
|
|
|
2021-01-20 01:44:17 +08:00
|
|
|
static int qcom_labibb_set_soft_start(struct regulator_dev *rdev)
|
|
|
|
{
|
|
|
|
struct labibb_regulator *vreg = rdev_get_drvdata(rdev);
|
|
|
|
u32 val = 0;
|
|
|
|
|
|
|
|
if (vreg->type == QCOM_IBB_TYPE)
|
|
|
|
val = vreg->dischg_sel;
|
|
|
|
else
|
|
|
|
val = vreg->soft_start_sel;
|
|
|
|
|
|
|
|
return regmap_write(rdev->regmap, rdev->desc->soft_start_reg, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int qcom_labibb_get_table_sel(const int *table, int sz, u32 value)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < sz; i++)
|
|
|
|
if (table[i] == value)
|
|
|
|
return i;
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* IBB discharge resistor values in KOhms */
|
|
|
|
static const int dischg_resistor_values[] = { 300, 64, 32, 16 };
|
|
|
|
|
|
|
|
/* Soft start time in microseconds */
|
|
|
|
static const int soft_start_values[] = { 200, 400, 600, 800 };
|
|
|
|
|
|
|
|
static int qcom_labibb_of_parse_cb(struct device_node *np,
|
|
|
|
const struct regulator_desc *desc,
|
|
|
|
struct regulator_config *config)
|
|
|
|
{
|
|
|
|
struct labibb_regulator *vreg = config->driver_data;
|
|
|
|
u32 dischg_kohms, soft_start_time;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = of_property_read_u32(np, "qcom,discharge-resistor-kohms",
|
|
|
|
&dischg_kohms);
|
|
|
|
if (ret)
|
|
|
|
dischg_kohms = 300;
|
|
|
|
|
|
|
|
ret = qcom_labibb_get_table_sel(dischg_resistor_values,
|
|
|
|
ARRAY_SIZE(dischg_resistor_values),
|
|
|
|
dischg_kohms);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
vreg->dischg_sel = (u8)ret;
|
|
|
|
|
|
|
|
ret = of_property_read_u32(np, "qcom,soft-start-us",
|
|
|
|
&soft_start_time);
|
|
|
|
if (ret)
|
|
|
|
soft_start_time = 200;
|
|
|
|
|
|
|
|
ret = qcom_labibb_get_table_sel(soft_start_values,
|
|
|
|
ARRAY_SIZE(soft_start_values),
|
|
|
|
soft_start_time);
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
vreg->soft_start_sel = (u8)ret;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-10-01 00:26:02 +08:00
|
|
|
static const struct regulator_ops qcom_labibb_ops = {
|
2020-06-22 20:41:10 +08:00
|
|
|
.enable = regulator_enable_regmap,
|
|
|
|
.disable = regulator_disable_regmap,
|
|
|
|
.is_enabled = regulator_is_enabled_regmap,
|
2021-01-14 03:42:08 +08:00
|
|
|
.set_voltage_sel = regulator_set_voltage_sel_regmap,
|
|
|
|
.get_voltage_sel = regulator_get_voltage_sel_regmap,
|
2021-01-20 01:44:15 +08:00
|
|
|
.list_voltage = regulator_list_voltage_linear,
|
|
|
|
.map_voltage = regulator_map_voltage_linear,
|
2021-01-20 01:44:17 +08:00
|
|
|
.set_active_discharge = regulator_set_active_discharge_regmap,
|
|
|
|
.set_pull_down = regulator_set_pull_down_regmap,
|
2021-01-20 01:44:16 +08:00
|
|
|
.set_current_limit = qcom_labibb_set_current_limit,
|
|
|
|
.get_current_limit = qcom_labibb_get_current_limit,
|
2021-01-20 01:44:17 +08:00
|
|
|
.set_soft_start = qcom_labibb_set_soft_start,
|
regulator: qcom-labibb: Implement short-circuit and over-current IRQs
Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are
very important for regulators like LAB and IBB, which are designed to
provide from very small to relatively big amounts of current to the
device (normally, a display).
Now that this regulator supports both voltage setting and current
limiting in this driver, to me it looked like being somehow essential
to provide support for SCP and OCP, for two reasons:
1. SCP is a drastic measure to prevent damaging "more" hardware in
the worst situations, if any was damaged, preventing potentially
drastic issues;
2. OCP is a great way to protect the hardware that we're powering
through these regulators as if anything bad happens, the HW will
draw more current than expected: in this case, the OCP interrupt
will fire and the regulators will be immediately shut down,
preventing hardware damage in many cases.
Both interrupts were successfully tested in a "sort-of" controlled
manner, with the following methodology:
Short-Circuit Protection (SCP):
1. Set LAB/IBB to 4.6/-1.4V, current limit 200mA/50mA;
2. Connect a 10 KOhm resistor to LAB/IBB by poking the right traces
on a FxTec Pro1 smartphone for a very brief time (in short words,
"just a rapid touch with flying wires");
3. The Short-Circuit protection trips: IRQ raises, regulators get
cut. Recovery OK, test repeated without rebooting, OK.
Over-Current Protection (OCP):
1. Set LAB/IBB to the expected voltage to power up the display of
a Sony Xperia XZ Premium smartphone (Sharp LS055D1SX04), set
current limit to LAB 200mA, IBB 50mA (the values that this
display unit needs are 200/800mA);
2. Boot the kernel: OCP fires. Recovery never happens because
the selected current limit is too low, but that's expected.
Test OK.
3. Set LAB/IBB to the expected current limits for XZ Premium
(LAB 200mA, IBB 800mA), but lower than expected voltage,
specifically LAB 5.4V, IBB -5.6V (instead of 5.6, -5.8V);
4. Boot the kernel: OCP fires. Recovery never happens because
the selected voltage (still in the working range limits)
is producing a current draw of more than 200mA on LAB.
Test OK.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
Link: https://lore.kernel.org/r/20210119174421.226541-6-angelogioacchino.delregno@somainline.org
Signed-off-by: Mark Brown <broonie@kernel.org>
2021-01-20 01:44:19 +08:00
|
|
|
.set_over_current_protection = qcom_labibb_set_ocp,
|
2020-06-22 20:41:10 +08:00
|
|
|
};
|
|
|
|
|
2020-10-01 00:26:02 +08:00
|
|
|
static const struct regulator_desc pmi8998_lab_desc = {
|
2020-06-22 20:41:10 +08:00
|
|
|
.enable_mask = LAB_ENABLE_CTL_MASK,
|
|
|
|
.enable_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_ENABLE_CTL),
|
|
|
|
.enable_val = LABIBB_CONTROL_ENABLE,
|
|
|
|
.enable_time = LAB_ENABLE_TIME,
|
|
|
|
.poll_enabled_time = LABIBB_POLL_ENABLED_TIME,
|
2021-01-20 01:44:17 +08:00
|
|
|
.soft_start_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_SOFT_START_CTL),
|
|
|
|
.pull_down_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_PD_CTL),
|
|
|
|
.pull_down_mask = LAB_PD_CTL_MASK,
|
|
|
|
.pull_down_val_on = LAB_PD_CTL_STRONG_PULL,
|
2021-01-14 03:42:08 +08:00
|
|
|
.vsel_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_VOLTAGE),
|
|
|
|
.vsel_mask = LAB_VOLTAGE_SET_MASK,
|
|
|
|
.apply_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_VOLTAGE),
|
|
|
|
.apply_bit = LABIBB_VOLTAGE_OVERRIDE_EN,
|
2021-01-20 01:44:16 +08:00
|
|
|
.csel_reg = (PMI8998_LAB_REG_BASE + REG_LABIBB_CURRENT_LIMIT),
|
|
|
|
.csel_mask = LAB_CURRENT_LIMIT_MASK,
|
|
|
|
.n_current_limits = 8,
|
2020-06-22 20:41:10 +08:00
|
|
|
.off_on_delay = LABIBB_OFF_ON_DELAY,
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.type = REGULATOR_VOLTAGE,
|
2021-01-20 01:44:15 +08:00
|
|
|
.min_uV = 4600000,
|
|
|
|
.uV_step = 100000,
|
2021-01-14 03:42:08 +08:00
|
|
|
.n_voltages = 16,
|
2020-06-22 20:41:10 +08:00
|
|
|
.ops = &qcom_labibb_ops,
|
2021-01-20 01:44:17 +08:00
|
|
|
.of_parse_cb = qcom_labibb_of_parse_cb,
|
2020-06-22 20:41:10 +08:00
|
|
|
};
|
|
|
|
|
2020-10-01 00:26:02 +08:00
|
|
|
static const struct regulator_desc pmi8998_ibb_desc = {
|
2020-06-22 20:41:10 +08:00
|
|
|
.enable_mask = IBB_ENABLE_CTL_MASK,
|
|
|
|
.enable_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_ENABLE_CTL),
|
|
|
|
.enable_val = LABIBB_CONTROL_ENABLE,
|
|
|
|
.enable_time = IBB_ENABLE_TIME,
|
|
|
|
.poll_enabled_time = LABIBB_POLL_ENABLED_TIME,
|
2021-01-20 01:44:17 +08:00
|
|
|
.soft_start_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_SOFT_START_CTL),
|
|
|
|
.active_discharge_off = 0,
|
|
|
|
.active_discharge_on = IBB_CTL_1_DISCHARGE_EN,
|
|
|
|
.active_discharge_mask = IBB_CTL_1_DISCHARGE_EN,
|
|
|
|
.active_discharge_reg = (PMI8998_IBB_REG_BASE + REG_IBB_PWRUP_PWRDN_CTL_1),
|
|
|
|
.pull_down_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_PD_CTL),
|
|
|
|
.pull_down_mask = IBB_PD_CTL_MASK,
|
|
|
|
.pull_down_val_on = IBB_PD_CTL_HALF_STRENGTH | IBB_PD_CTL_EN,
|
2021-01-14 03:42:08 +08:00
|
|
|
.vsel_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_VOLTAGE),
|
|
|
|
.vsel_mask = IBB_VOLTAGE_SET_MASK,
|
|
|
|
.apply_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_VOLTAGE),
|
|
|
|
.apply_bit = LABIBB_VOLTAGE_OVERRIDE_EN,
|
2021-01-20 01:44:16 +08:00
|
|
|
.csel_reg = (PMI8998_IBB_REG_BASE + REG_LABIBB_CURRENT_LIMIT),
|
|
|
|
.csel_mask = IBB_CURRENT_LIMIT_MASK,
|
|
|
|
.n_current_limits = 32,
|
2020-06-22 20:41:10 +08:00
|
|
|
.off_on_delay = LABIBB_OFF_ON_DELAY,
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
.type = REGULATOR_VOLTAGE,
|
2021-01-20 01:44:15 +08:00
|
|
|
.min_uV = 1400000,
|
|
|
|
.uV_step = 100000,
|
2021-01-14 03:42:08 +08:00
|
|
|
.n_voltages = 64,
|
2020-06-22 20:41:10 +08:00
|
|
|
.ops = &qcom_labibb_ops,
|
2021-01-20 01:44:17 +08:00
|
|
|
.of_parse_cb = qcom_labibb_of_parse_cb,
|
2020-06-22 20:41:10 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct labibb_regulator_data pmi8998_labibb_data[] = {
|
|
|
|
{"lab", QCOM_LAB_TYPE, PMI8998_LAB_REG_BASE, &pmi8998_lab_desc},
|
|
|
|
{"ibb", QCOM_IBB_TYPE, PMI8998_IBB_REG_BASE, &pmi8998_ibb_desc},
|
|
|
|
{ },
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct of_device_id qcom_labibb_match[] = {
|
|
|
|
{ .compatible = "qcom,pmi8998-lab-ibb", .data = &pmi8998_labibb_data},
|
|
|
|
{ },
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(of, qcom_labibb_match);
|
|
|
|
|
|
|
|
static int qcom_labibb_regulator_probe(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
struct labibb_regulator *vreg;
|
|
|
|
struct device *dev = &pdev->dev;
|
|
|
|
struct regulator_config cfg = {};
|
regulator: qcom-labibb: Implement short-circuit and over-current IRQs
Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are
very important for regulators like LAB and IBB, which are designed to
provide from very small to relatively big amounts of current to the
device (normally, a display).
Now that this regulator supports both voltage setting and current
limiting in this driver, to me it looked like being somehow essential
to provide support for SCP and OCP, for two reasons:
1. SCP is a drastic measure to prevent damaging "more" hardware in
the worst situations, if any was damaged, preventing potentially
drastic issues;
2. OCP is a great way to protect the hardware that we're powering
through these regulators as if anything bad happens, the HW will
draw more current than expected: in this case, the OCP interrupt
will fire and the regulators will be immediately shut down,
preventing hardware damage in many cases.
Both interrupts were successfully tested in a "sort-of" controlled
manner, with the following methodology:
Short-Circuit Protection (SCP):
1. Set LAB/IBB to 4.6/-1.4V, current limit 200mA/50mA;
2. Connect a 10 KOhm resistor to LAB/IBB by poking the right traces
on a FxTec Pro1 smartphone for a very brief time (in short words,
"just a rapid touch with flying wires");
3. The Short-Circuit protection trips: IRQ raises, regulators get
cut. Recovery OK, test repeated without rebooting, OK.
Over-Current Protection (OCP):
1. Set LAB/IBB to the expected voltage to power up the display of
a Sony Xperia XZ Premium smartphone (Sharp LS055D1SX04), set
current limit to LAB 200mA, IBB 50mA (the values that this
display unit needs are 200/800mA);
2. Boot the kernel: OCP fires. Recovery never happens because
the selected current limit is too low, but that's expected.
Test OK.
3. Set LAB/IBB to the expected current limits for XZ Premium
(LAB 200mA, IBB 800mA), but lower than expected voltage,
specifically LAB 5.4V, IBB -5.6V (instead of 5.6, -5.8V);
4. Boot the kernel: OCP fires. Recovery never happens because
the selected voltage (still in the working range limits)
is producing a current draw of more than 200mA on LAB.
Test OK.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
Link: https://lore.kernel.org/r/20210119174421.226541-6-angelogioacchino.delregno@somainline.org
Signed-off-by: Mark Brown <broonie@kernel.org>
2021-01-20 01:44:19 +08:00
|
|
|
struct device_node *reg_node;
|
2020-06-22 20:41:10 +08:00
|
|
|
const struct of_device_id *match;
|
|
|
|
const struct labibb_regulator_data *reg_data;
|
|
|
|
struct regmap *reg_regmap;
|
|
|
|
unsigned int type;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
reg_regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
|
|
|
if (!reg_regmap) {
|
|
|
|
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
|
|
|
|
match = of_match_device(qcom_labibb_match, &pdev->dev);
|
|
|
|
if (!match)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
for (reg_data = match->data; reg_data->name; reg_data++) {
|
regulator: qcom-labibb: Implement short-circuit and over-current IRQs
Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are
very important for regulators like LAB and IBB, which are designed to
provide from very small to relatively big amounts of current to the
device (normally, a display).
Now that this regulator supports both voltage setting and current
limiting in this driver, to me it looked like being somehow essential
to provide support for SCP and OCP, for two reasons:
1. SCP is a drastic measure to prevent damaging "more" hardware in
the worst situations, if any was damaged, preventing potentially
drastic issues;
2. OCP is a great way to protect the hardware that we're powering
through these regulators as if anything bad happens, the HW will
draw more current than expected: in this case, the OCP interrupt
will fire and the regulators will be immediately shut down,
preventing hardware damage in many cases.
Both interrupts were successfully tested in a "sort-of" controlled
manner, with the following methodology:
Short-Circuit Protection (SCP):
1. Set LAB/IBB to 4.6/-1.4V, current limit 200mA/50mA;
2. Connect a 10 KOhm resistor to LAB/IBB by poking the right traces
on a FxTec Pro1 smartphone for a very brief time (in short words,
"just a rapid touch with flying wires");
3. The Short-Circuit protection trips: IRQ raises, regulators get
cut. Recovery OK, test repeated without rebooting, OK.
Over-Current Protection (OCP):
1. Set LAB/IBB to the expected voltage to power up the display of
a Sony Xperia XZ Premium smartphone (Sharp LS055D1SX04), set
current limit to LAB 200mA, IBB 50mA (the values that this
display unit needs are 200/800mA);
2. Boot the kernel: OCP fires. Recovery never happens because
the selected current limit is too low, but that's expected.
Test OK.
3. Set LAB/IBB to the expected current limits for XZ Premium
(LAB 200mA, IBB 800mA), but lower than expected voltage,
specifically LAB 5.4V, IBB -5.6V (instead of 5.6, -5.8V);
4. Boot the kernel: OCP fires. Recovery never happens because
the selected voltage (still in the working range limits)
is producing a current draw of more than 200mA on LAB.
Test OK.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
Link: https://lore.kernel.org/r/20210119174421.226541-6-angelogioacchino.delregno@somainline.org
Signed-off-by: Mark Brown <broonie@kernel.org>
2021-01-20 01:44:19 +08:00
|
|
|
char *sc_irq_name;
|
|
|
|
int irq = 0;
|
2020-06-22 20:41:10 +08:00
|
|
|
|
|
|
|
/* Validate if the type of regulator is indeed
|
|
|
|
* what's mentioned in DT.
|
|
|
|
*/
|
|
|
|
ret = regmap_read(reg_regmap, reg_data->base + REG_PERPH_TYPE,
|
|
|
|
&type);
|
|
|
|
if (ret < 0) {
|
|
|
|
dev_err(dev,
|
|
|
|
"Peripheral type read failed ret=%d\n",
|
|
|
|
ret);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (WARN_ON((type != QCOM_LAB_TYPE) && (type != QCOM_IBB_TYPE)) ||
|
|
|
|
WARN_ON(type != reg_data->type))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
vreg = devm_kzalloc(&pdev->dev, sizeof(*vreg),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!vreg)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
regulator: qcom-labibb: Implement short-circuit and over-current IRQs
Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are
very important for regulators like LAB and IBB, which are designed to
provide from very small to relatively big amounts of current to the
device (normally, a display).
Now that this regulator supports both voltage setting and current
limiting in this driver, to me it looked like being somehow essential
to provide support for SCP and OCP, for two reasons:
1. SCP is a drastic measure to prevent damaging "more" hardware in
the worst situations, if any was damaged, preventing potentially
drastic issues;
2. OCP is a great way to protect the hardware that we're powering
through these regulators as if anything bad happens, the HW will
draw more current than expected: in this case, the OCP interrupt
will fire and the regulators will be immediately shut down,
preventing hardware damage in many cases.
Both interrupts were successfully tested in a "sort-of" controlled
manner, with the following methodology:
Short-Circuit Protection (SCP):
1. Set LAB/IBB to 4.6/-1.4V, current limit 200mA/50mA;
2. Connect a 10 KOhm resistor to LAB/IBB by poking the right traces
on a FxTec Pro1 smartphone for a very brief time (in short words,
"just a rapid touch with flying wires");
3. The Short-Circuit protection trips: IRQ raises, regulators get
cut. Recovery OK, test repeated without rebooting, OK.
Over-Current Protection (OCP):
1. Set LAB/IBB to the expected voltage to power up the display of
a Sony Xperia XZ Premium smartphone (Sharp LS055D1SX04), set
current limit to LAB 200mA, IBB 50mA (the values that this
display unit needs are 200/800mA);
2. Boot the kernel: OCP fires. Recovery never happens because
the selected current limit is too low, but that's expected.
Test OK.
3. Set LAB/IBB to the expected current limits for XZ Premium
(LAB 200mA, IBB 800mA), but lower than expected voltage,
specifically LAB 5.4V, IBB -5.6V (instead of 5.6, -5.8V);
4. Boot the kernel: OCP fires. Recovery never happens because
the selected voltage (still in the working range limits)
is producing a current draw of more than 200mA on LAB.
Test OK.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
Link: https://lore.kernel.org/r/20210119174421.226541-6-angelogioacchino.delregno@somainline.org
Signed-off-by: Mark Brown <broonie@kernel.org>
2021-01-20 01:44:19 +08:00
|
|
|
sc_irq_name = devm_kasprintf(dev, GFP_KERNEL,
|
|
|
|
"%s-short-circuit",
|
|
|
|
reg_data->name);
|
|
|
|
if (!sc_irq_name)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
reg_node = of_get_child_by_name(pdev->dev.of_node,
|
|
|
|
reg_data->name);
|
|
|
|
if (!reg_node)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
/* The Short Circuit interrupt is critical */
|
|
|
|
irq = of_irq_get_byname(reg_node, "sc-err");
|
|
|
|
if (irq <= 0) {
|
|
|
|
if (irq == 0)
|
|
|
|
irq = -EINVAL;
|
|
|
|
|
|
|
|
return dev_err_probe(vreg->dev, irq,
|
|
|
|
"Short-circuit irq not found.\n");
|
|
|
|
}
|
|
|
|
vreg->sc_irq = irq;
|
|
|
|
|
|
|
|
/* OverCurrent Protection IRQ is optional */
|
|
|
|
irq = of_irq_get_byname(reg_node, "ocp");
|
|
|
|
vreg->ocp_irq = irq;
|
|
|
|
vreg->ocp_irq_count = 0;
|
|
|
|
of_node_put(reg_node);
|
|
|
|
|
2020-06-22 20:41:10 +08:00
|
|
|
vreg->regmap = reg_regmap;
|
|
|
|
vreg->dev = dev;
|
|
|
|
vreg->base = reg_data->base;
|
|
|
|
vreg->type = reg_data->type;
|
regulator: qcom-labibb: Implement short-circuit and over-current IRQs
Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are
very important for regulators like LAB and IBB, which are designed to
provide from very small to relatively big amounts of current to the
device (normally, a display).
Now that this regulator supports both voltage setting and current
limiting in this driver, to me it looked like being somehow essential
to provide support for SCP and OCP, for two reasons:
1. SCP is a drastic measure to prevent damaging "more" hardware in
the worst situations, if any was damaged, preventing potentially
drastic issues;
2. OCP is a great way to protect the hardware that we're powering
through these regulators as if anything bad happens, the HW will
draw more current than expected: in this case, the OCP interrupt
will fire and the regulators will be immediately shut down,
preventing hardware damage in many cases.
Both interrupts were successfully tested in a "sort-of" controlled
manner, with the following methodology:
Short-Circuit Protection (SCP):
1. Set LAB/IBB to 4.6/-1.4V, current limit 200mA/50mA;
2. Connect a 10 KOhm resistor to LAB/IBB by poking the right traces
on a FxTec Pro1 smartphone for a very brief time (in short words,
"just a rapid touch with flying wires");
3. The Short-Circuit protection trips: IRQ raises, regulators get
cut. Recovery OK, test repeated without rebooting, OK.
Over-Current Protection (OCP):
1. Set LAB/IBB to the expected voltage to power up the display of
a Sony Xperia XZ Premium smartphone (Sharp LS055D1SX04), set
current limit to LAB 200mA, IBB 50mA (the values that this
display unit needs are 200/800mA);
2. Boot the kernel: OCP fires. Recovery never happens because
the selected current limit is too low, but that's expected.
Test OK.
3. Set LAB/IBB to the expected current limits for XZ Premium
(LAB 200mA, IBB 800mA), but lower than expected voltage,
specifically LAB 5.4V, IBB -5.6V (instead of 5.6, -5.8V);
4. Boot the kernel: OCP fires. Recovery never happens because
the selected voltage (still in the working range limits)
is producing a current draw of more than 200mA on LAB.
Test OK.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
Link: https://lore.kernel.org/r/20210119174421.226541-6-angelogioacchino.delregno@somainline.org
Signed-off-by: Mark Brown <broonie@kernel.org>
2021-01-20 01:44:19 +08:00
|
|
|
INIT_DELAYED_WORK(&vreg->sc_recovery_work,
|
|
|
|
qcom_labibb_sc_recovery_worker);
|
|
|
|
|
|
|
|
if (vreg->ocp_irq > 0)
|
|
|
|
INIT_DELAYED_WORK(&vreg->ocp_recovery_work,
|
|
|
|
qcom_labibb_ocp_recovery_worker);
|
2020-06-22 20:41:10 +08:00
|
|
|
|
2021-01-20 01:44:16 +08:00
|
|
|
switch (vreg->type) {
|
|
|
|
case QCOM_LAB_TYPE:
|
|
|
|
/* LAB Limits: 200-1600mA */
|
|
|
|
vreg->uA_limits.uA_min = 200000;
|
|
|
|
vreg->uA_limits.uA_step = 200000;
|
|
|
|
vreg->uA_limits.ovr_val = LAB_CURRENT_LIMIT_OVERRIDE_EN;
|
|
|
|
break;
|
|
|
|
case QCOM_IBB_TYPE:
|
|
|
|
/* IBB Limits: 0-1550mA */
|
|
|
|
vreg->uA_limits.uA_min = 0;
|
|
|
|
vreg->uA_limits.uA_step = 50000;
|
|
|
|
vreg->uA_limits.ovr_val = 0; /* No override bit */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2020-06-22 20:41:10 +08:00
|
|
|
memcpy(&vreg->desc, reg_data->desc, sizeof(vreg->desc));
|
|
|
|
vreg->desc.of_match = reg_data->name;
|
|
|
|
vreg->desc.name = reg_data->name;
|
|
|
|
|
|
|
|
cfg.dev = vreg->dev;
|
|
|
|
cfg.driver_data = vreg;
|
|
|
|
cfg.regmap = vreg->regmap;
|
|
|
|
|
|
|
|
vreg->rdev = devm_regulator_register(vreg->dev, &vreg->desc,
|
|
|
|
&cfg);
|
|
|
|
|
|
|
|
if (IS_ERR(vreg->rdev)) {
|
|
|
|
dev_err(dev, "qcom_labibb: error registering %s : %d\n",
|
|
|
|
reg_data->name, ret);
|
|
|
|
return PTR_ERR(vreg->rdev);
|
|
|
|
}
|
regulator: qcom-labibb: Implement short-circuit and over-current IRQs
Short-Circuit Protection (SCP) and Over-Current Protection (OCP) are
very important for regulators like LAB and IBB, which are designed to
provide from very small to relatively big amounts of current to the
device (normally, a display).
Now that this regulator supports both voltage setting and current
limiting in this driver, to me it looked like being somehow essential
to provide support for SCP and OCP, for two reasons:
1. SCP is a drastic measure to prevent damaging "more" hardware in
the worst situations, if any was damaged, preventing potentially
drastic issues;
2. OCP is a great way to protect the hardware that we're powering
through these regulators as if anything bad happens, the HW will
draw more current than expected: in this case, the OCP interrupt
will fire and the regulators will be immediately shut down,
preventing hardware damage in many cases.
Both interrupts were successfully tested in a "sort-of" controlled
manner, with the following methodology:
Short-Circuit Protection (SCP):
1. Set LAB/IBB to 4.6/-1.4V, current limit 200mA/50mA;
2. Connect a 10 KOhm resistor to LAB/IBB by poking the right traces
on a FxTec Pro1 smartphone for a very brief time (in short words,
"just a rapid touch with flying wires");
3. The Short-Circuit protection trips: IRQ raises, regulators get
cut. Recovery OK, test repeated without rebooting, OK.
Over-Current Protection (OCP):
1. Set LAB/IBB to the expected voltage to power up the display of
a Sony Xperia XZ Premium smartphone (Sharp LS055D1SX04), set
current limit to LAB 200mA, IBB 50mA (the values that this
display unit needs are 200/800mA);
2. Boot the kernel: OCP fires. Recovery never happens because
the selected current limit is too low, but that's expected.
Test OK.
3. Set LAB/IBB to the expected current limits for XZ Premium
(LAB 200mA, IBB 800mA), but lower than expected voltage,
specifically LAB 5.4V, IBB -5.6V (instead of 5.6, -5.8V);
4. Boot the kernel: OCP fires. Recovery never happens because
the selected voltage (still in the working range limits)
is producing a current draw of more than 200mA on LAB.
Test OK.
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@somainline.org>
Link: https://lore.kernel.org/r/20210119174421.226541-6-angelogioacchino.delregno@somainline.org
Signed-off-by: Mark Brown <broonie@kernel.org>
2021-01-20 01:44:19 +08:00
|
|
|
|
|
|
|
ret = devm_request_threaded_irq(vreg->dev, vreg->sc_irq, NULL,
|
|
|
|
qcom_labibb_sc_isr,
|
|
|
|
IRQF_ONESHOT |
|
|
|
|
IRQF_TRIGGER_RISING,
|
|
|
|
sc_irq_name, vreg);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2020-06-22 20:41:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct platform_driver qcom_labibb_regulator_driver = {
|
|
|
|
.driver = {
|
|
|
|
.name = "qcom-lab-ibb-regulator",
|
|
|
|
.of_match_table = qcom_labibb_match,
|
|
|
|
},
|
|
|
|
.probe = qcom_labibb_regulator_probe,
|
|
|
|
};
|
|
|
|
module_platform_driver(qcom_labibb_regulator_driver);
|
|
|
|
|
|
|
|
MODULE_DESCRIPTION("Qualcomm labibb driver");
|
|
|
|
MODULE_AUTHOR("Nisha Kumari <nishakumari@codeaurora.org>");
|
|
|
|
MODULE_AUTHOR("Sumit Semwal <sumit.semwal@linaro.org>");
|
|
|
|
MODULE_LICENSE("GPL v2");
|