From 400b5ba8dd6718a4ba4a0caecfd6692998718574 Mon Sep 17 00:00:00 2001 From: exer <98556122+ekkusa@users.noreply.github.com> Date: Sun, 15 Jan 2023 11:05:54 +0000 Subject: [PATCH] drivers/regulator: Import Sony Changes *52.1.A.3.74 Signed-off-by: Sorayukii --- drivers/regulator/Kconfig | 4 + drivers/regulator/core.c | 27 + drivers/regulator/qpnp-labibb-regulator.c | 1053 ++++++++++++++++++++- drivers/regulator/qpnp-regulator.c | 42 + 4 files changed, 1123 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 1bd67e66a04f..3b84d84221e2 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1088,5 +1088,9 @@ config REGULATOR_STUB Clients can use the real regulator device names with proper constraint checking while the real driver is being developed. +config SOMC_LCD_OCP_ENABLED + bool "enable SoMC LCD OCP function" + help + Select this to enable SoMC LCD Over current protection function. endif diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 75a80f472d4f..0b45e3a43233 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3532,6 +3532,33 @@ int regulator_allow_bypass(struct regulator *regulator, bool enable) } EXPORT_SYMBOL_GPL(regulator_allow_bypass); +/* + * regulator_register_ocp_notification - register ocp notification + * @regulator: regulator source + * @notification: pointer of client ocp_notification + * + */ +int regulator_register_ocp_notification(struct regulator *regulator, + struct regulator_ocp_notification *notification) +{ + struct regulator_dev *rdev = regulator->rdev; + int ret; + + mutex_lock(&rdev->mutex); + + /* sanity check */ + if (!rdev->desc->ops->register_ocp_notification) { + ret = -EINVAL; + goto out; + } + + ret = rdev->desc->ops->register_ocp_notification(rdev, notification); +out: + mutex_unlock(&rdev->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_register_ocp_notification); + /** * regulator_register_notifier - register regulator event notifier * @regulator: regulator source diff --git a/drivers/regulator/qpnp-labibb-regulator.c b/drivers/regulator/qpnp-labibb-regulator.c index d72af2095593..f873a52a2100 100644 --- a/drivers/regulator/qpnp-labibb-regulator.c +++ b/drivers/regulator/qpnp-labibb-regulator.c @@ -31,6 +31,11 @@ #include #include #include +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +#include +#include +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + #include #include @@ -51,6 +56,14 @@ /* LAB register offset definitions */ #define REG_LAB_STATUS1 0x08 +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +#define REG_LAB_INT_SET_TYPE 0x11 +#define REG_LAB_INT_POLARITY_HIGH 0x12 +#define REG_LAB_INT_POLARITY_LOW 0x13 +#define REG_LAB_INT_LATCHED_CLR 0x14 +#define REG_LAB_INT_EN_SET 0x15 +#define REG_LAB_INT_EN_CLR 0x16 +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ #define REG_LAB_SWIRE_PGM_CTL 0x40 #define REG_LAB_VOLTAGE 0x41 #define REG_LAB_RING_SUPPRESSION_CTL 0x42 @@ -82,6 +95,33 @@ #define LAB_STATUS1_VREG_OK_BIT BIT(7) #define LAB_STATUS1_SC_DETECT_BIT BIT(6) +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +/* REG_LAB_INT_SET_TYPE */ +#define LAB_INT_SET_TYPE_VREG_OK_MASK BIT(0) +#define LAB_INT_SET_TYPE_VREG_OK_LEVEL 0 +#define LAB_INT_SET_TYPE_VREG_OK_EDGE 1 + +/* REG_LAB_INT_POLARITY_HIGH */ +#define LAB_INT_PRY_HIGH_VREG_OK_MASK BIT(0) +#define LAB_INT_PRY_HIGH_VREG_OK 0 + +/* REG_LAB_INT_POLARITY_LOW */ +#define LAB_INT_PRY_LOW_VREG_OK_MASK BIT(0) +#define LAB_INT_PRY_LOW_VREG_OK 1 + +/* REG_LAB_INT_LATCHED_CLR */ +#define LAB_INT_LATCHED_CLR_VREG_MASK BIT(0) +#define LAB_INT_LATCHED_CLR_VREG 1 + +/* REG_LAB_INT_EN_SET */ +#define LAB_INT_EN_SET_VREG_OK_MASK BIT(0) +#define LAB_INT_EN_SET_VREG_OK 1 + +/* REG_LAB_INT_EN_CLR */ +#define LAB_INT_EN_CLR_VREG_OK_MASK BIT(0) +#define LAB_INT_EN_CLR_VREG_OK 1 +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + /* REG_LAB_SWIRE_PGM_CTL */ #define LAB_EN_SWIRE_PGM_VOUT BIT(7) #define LAB_EN_SWIRE_PGM_PD BIT(6) @@ -113,6 +153,10 @@ #define LAB_CURRENT_LIMIT_EN_BIT BIT(7) #define LAB_OVERRIDE_CURRENT_MAX_BIT BIT(3) +#ifdef SOMC_LABIBB_REGULATOR_ORG_IMPL +#define LAB_CURRENT_LIMIT_OVERRIDE BIT(3) +#endif /* SOMC_LABIBB_REGULATOR_ORG_IMPL */ + /* REG_LAB_CURRENT_SENSE */ #define LAB_CURRENT_SENSE_GAIN_MASK GENMASK(1, 0) @@ -158,6 +202,14 @@ /* IBB register offset definitions */ #define REG_IBB_REVISION4 0x03 #define REG_IBB_STATUS1 0x08 +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +#define REG_IBB_INT_SET_TYPE 0x11 +#define REG_IBB_INT_POLARITY_HIGH 0x12 +#define REG_IBB_INT_POLARITY_LOW 0x13 +#define REG_IBB_INT_LATCHED_CLR 0x14 +#define REG_IBB_INT_EN_SET 0x15 +#define REG_IBB_INT_EN_CLR 0x16 +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ #define REG_IBB_VOLTAGE 0x41 #define REG_IBB_RING_SUPPRESSION_CTL 0x42 #define REG_IBB_LCD_AMOLED_SEL 0x44 @@ -193,6 +245,33 @@ #define IBB_STATUS1_VREG_OK_BIT BIT(7) #define IBB_STATUS1_SC_DETECT_BIT BIT(6) +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +/* REG_IBB_INT_SET_TYPE */ +#define IBB_INT_SET_TYPE_VREG_OK_MASK BIT(0) +#define IBB_INT_SET_TYPE_VREG_OK_LEVEL 0 +#define IBB_INT_SET_TYPE_VREG_OK_EDGE 1 + +/* REG_IBB_INT_POLARITY_HIGH */ +#define IBB_INT_PRY_HIGH_VREG_OK_MASK BIT(0) +#define IBB_INT_PRY_HIGH_VREG_OK 1 + +/* REG_IBB_INT_POLARITY_LOW */ +#define IBB_INT_PRY_LOW_VREG_OK_MASK BIT(0) +#define IBB_INT_PRY_LOW_VREG_OK 0 + +/* REG_IBB_INT_LATCHED_CLR */ +#define IBB_INT_LATCHED_CLR_VREG_MASK BIT(0) +#define IBB_INT_LATCHED_CLR_VREG 1 + +/* REG_IBB_INT_EN_SET */ +#define IBB_INT_EN_SET_VREG_OK_MASK BIT(0) +#define IBB_INT_EN_SET_VREG_OK 1 + +/* REG_IBB_INT_EN_CLR */ +#define IBB_INT_EN_CLR_VREG_OK_MASK BIT(0) +#define IBB_INT_EN_CLR_VREG_OK 1 +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + /* REG_IBB_VOLTAGE */ #define IBB_VOLTAGE_OVERRIDE_EN BIT(7) #define IBB_VOLTAGE_SET_MASK GENMASK(5, 0) @@ -234,6 +313,10 @@ #define IBB_ILIMIT_COUNT_CYC8 0 #define IBB_CURRENT_MAX_500MA 0xA +#ifdef SOMC_LABIBB_REGULATOR_ORG_IMPL +#define IBB_CURRENT_LIMIT_VALUE 16 +#endif /* SOMC_LABIBB_REGULATOR_ORG_IMPL */ + /* REG_IBB_PS_CTL */ #define IBB_PS_CTL_EN 0x85 @@ -310,6 +393,49 @@ #define IBB_DIS_DLY_MASK GENMASK(1, 0) #define IBB_WAIT_MBG_OK BIT(2) +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +/* enable_irq */ +#define ENABLE_IRQ(irq_num) \ +do { \ + pr_debug("ENABLE_IRQ: %s\n", __func__); \ + enable_irq(irq_num); \ +} while (0) + +/* disable_irq */ +#define DISABLE_IRQ(irq_num) \ +do { \ + pr_debug("DISABLE_IRQ_NOSYNC: %s\n", __func__); \ + disable_irq_nosync(irq_num); \ +} while (0) + +#define IRQF_LAB_FLAGS (IRQF_DISABLED | IRQF_ONESHOT | IRQF_TRIGGER_LOW) +#define IRQF_IBB_FLAGS (IRQF_DISABLED | IRQF_ONESHOT | IRQF_TRIGGER_HIGH) + +#define CHATTER_CNT_START 1 +#define DEFAULT_TARGET_CHATTER_CNT 3 +#define DEFAULT_TARGET_CHATTER_INTERVAL 500 +#define POWER_OFF_RETRY_INTERVAL 500 + +#define VREG_WORKER_ACTIVE true +#define VREG_WORKER_PASSIVE false + +static irqreturn_t ibb_vreg_handler(int irq, void *_chip); +static irqreturn_t lab_vreg_handler(int irq, void *_chip); + +static struct labibb_vreg_status_ctrl { + struct delayed_work vreg_check_work; + struct qpnp_labibb *labibb; + int current_chatter_cnt; + int target_chatter_cnt; + int target_chatter_check_interval; + bool vreg_check_working; +} labibb_vreg_check; + +static int qpnp_ibb_register_irq(struct device_node *child, + struct qpnp_labibb *labibb); + +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + /* Constants */ #define SWIRE_DEFAULT_2ND_CMD_DLY_MS 20 #define SWIRE_DEFAULT_IBB_PS_ENABLE_DLY_MS 200 @@ -325,6 +451,9 @@ * supported by qpnp_labibb_regulator */ enum qpnp_labibb_mode { +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + QPNP_LABIBB_STANDALONE_MODE = 1, +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ QPNP_LABIBB_LCD_MODE, QPNP_LABIBB_AMOLED_MODE, QPNP_LABIBB_MAX_MODE, @@ -613,6 +742,10 @@ struct qpnp_labibb { struct delayed_work sc_err_recovery_work; struct hrtimer sc_err_check_timer; int sc_err_count; +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + int lab_vreg_irq; + int ibb_vreg_irq; +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ bool standalone; bool ttw_en; bool in_ttw_mode; @@ -1422,6 +1555,299 @@ static int qpnp_labibb_get_matching_idx(const char *val) return -EINVAL; } +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +static int qpnp_lab_interrupt_enable_ctl(struct qpnp_labibb *labibb, + bool inWork) +{ + int rc = 0; + + if (labibb_vreg_check.vreg_check_working && !inWork) { + pr_debug("%s: vreg_check_worker is already being processed.\n", + __func__); + goto exit; + } + + /* lab int latched clr */ + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_INT_LATCHED_CLR, + LAB_INT_LATCHED_CLR_VREG_MASK, + LAB_INT_LATCHED_CLR_VREG); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_LAB_INT_LATCHED_CLR, rc); + goto exit; + } + /* lab int en set */ + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_INT_EN_SET, + LAB_INT_EN_SET_VREG_OK_MASK, + LAB_INT_EN_SET_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_LAB_INT_EN_SET, rc); + goto exit; + } + ENABLE_IRQ(labibb->lab_vreg_irq); +exit: + return rc; +} + +static int qpnp_lab_interrupt_disable_ctl(struct qpnp_labibb *labibb) +{ + int rc = 0; + + if (labibb_vreg_check.vreg_check_working) { + pr_err("%s: vreg_check_worker is already being processed.\n", + __func__); + } + + DISABLE_IRQ(labibb->lab_vreg_irq); + /* lab int en clr */ + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_INT_EN_CLR, + LAB_INT_EN_CLR_VREG_OK_MASK, + LAB_INT_EN_CLR_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_LAB_INT_EN_CLR, rc); + goto exit; + } +exit: + return rc; +} + +static int qpnp_ibb_interrupt_enable_ctl(struct qpnp_labibb *labibb, + bool inWork) +{ + int rc = 0; + + if (labibb_vreg_check.vreg_check_working && !inWork) { + pr_debug("%s: vreg_check_worker is already being processed.\n", + __func__); + goto exit; + } + + /* ibb int latched clr */ + rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + + REG_IBB_INT_LATCHED_CLR, + IBB_INT_LATCHED_CLR_VREG_MASK, + IBB_INT_LATCHED_CLR_VREG); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_IBB_INT_LATCHED_CLR, rc); + goto exit; + } + /* ibb int en set */ + rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + + REG_IBB_INT_EN_SET, + IBB_INT_EN_SET_VREG_OK_MASK, + IBB_INT_EN_SET_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_IBB_INT_EN_SET, rc); + goto exit; + } + ENABLE_IRQ(labibb->ibb_vreg_irq); +exit: + return rc; +} + +static int qpnp_ibb_interrupt_disable_ctl(struct qpnp_labibb *labibb) +{ + int rc = 0; + + if (labibb_vreg_check.vreg_check_working) { + pr_err("%s: vreg_check_worker is already being processed.\n", + __func__); + } + + DISABLE_IRQ(labibb->ibb_vreg_irq); + /* ibb int en clr */ + rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + + REG_IBB_INT_EN_CLR, + IBB_INT_EN_CLR_VREG_OK_MASK, + IBB_INT_EN_CLR_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_IBB_INT_EN_CLR, rc); + goto exit; + } +exit: + return rc; +} + +static int qpnp_labibb_interrupt_enable_ctl(struct qpnp_labibb *labibb, + bool inWork) +{ + int rc; + + rc = qpnp_lab_interrupt_enable_ctl(labibb, inWork); + if (rc) { + pr_err("%s: qpnp_lab_interrupt_enable_ctl failed rc = %d\n", + __func__, rc); + goto exit; + } + + rc = qpnp_ibb_interrupt_enable_ctl(labibb, inWork); + if (rc) { + pr_err("%s: qpnp_ibb_interrupt_enable_ctl failed rc = %d\n", + __func__, rc); + goto exit; + } + +exit: + return rc; +} + +static int qpnp_labibb_interrupt_disable_ctl(struct qpnp_labibb *labibb) +{ + int rc; + + rc = qpnp_lab_interrupt_disable_ctl(labibb); + if (rc) { + pr_err("%s: qpnp_lab_interrupt_disable_ctl failed rc = %d\n", + __func__, rc); + goto exit; + } + + rc = qpnp_ibb_interrupt_disable_ctl(labibb); + if (rc) { + pr_err("%s: qpnp_ibb_interrupt_disable_ctl failed rc = %d\n", + __func__, rc); + goto exit; + } + +exit: + return rc; +} + +static int qpnp_lab_request_interrupt(struct qpnp_labibb *labibb) +{ + int rc = 0; + + /* lab int set type */ + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_INT_SET_TYPE, + LAB_INT_SET_TYPE_VREG_OK_MASK, + LAB_INT_SET_TYPE_VREG_OK_LEVEL); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_LAB_INT_SET_TYPE, rc); + goto exit; + } + + /* lab int polarity high */ + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_INT_POLARITY_HIGH, + LAB_INT_PRY_HIGH_VREG_OK_MASK, + LAB_INT_PRY_HIGH_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_LAB_INT_POLARITY_HIGH, rc); + goto exit; + } + + /* lab int polarity low */ + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_INT_POLARITY_LOW, + LAB_INT_PRY_LOW_VREG_OK_MASK, + LAB_INT_PRY_LOW_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_LAB_INT_POLARITY_LOW, rc); + goto exit; + } + + /* lab int en set */ + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_INT_EN_SET, + LAB_INT_EN_SET_VREG_OK_MASK, + LAB_INT_EN_SET_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_LAB_INT_EN_SET, rc); + goto exit; + } + + /* lab int latched clr */ + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_INT_LATCHED_CLR, + LAB_INT_LATCHED_CLR_VREG_MASK, + LAB_INT_LATCHED_CLR_VREG); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_LAB_INT_LATCHED_CLR, rc); + goto exit; + } + +exit: + return rc; +} + +static int qpnp_ibb_request_interrupt(struct qpnp_labibb *labibb) +{ + int rc = 0; + + /* ibb int set type */ + rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + + REG_IBB_INT_SET_TYPE, + IBB_INT_SET_TYPE_VREG_OK_MASK, + IBB_INT_SET_TYPE_VREG_OK_LEVEL); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_IBB_INT_SET_TYPE, rc); + goto exit; + } + + /* ibb int polarity high */ + rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + + REG_IBB_INT_POLARITY_HIGH, + IBB_INT_PRY_HIGH_VREG_OK_MASK, + IBB_INT_PRY_HIGH_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_IBB_INT_POLARITY_HIGH, rc); + goto exit; + } + + /* IBB int polarity low */ + rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + + REG_IBB_INT_POLARITY_LOW, + IBB_INT_PRY_LOW_VREG_OK_MASK, + IBB_INT_PRY_LOW_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_IBB_INT_POLARITY_LOW, rc); + goto exit; + } + + /* ibb int en set */ + rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + + REG_IBB_INT_EN_SET, + IBB_INT_EN_SET_VREG_OK_MASK, + IBB_INT_EN_SET_VREG_OK); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_IBB_INT_EN_SET, rc); + goto exit; + } + + /* ibb int latched clr */ + rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + + REG_IBB_INT_LATCHED_CLR, + IBB_INT_LATCHED_CLR_VREG_MASK, + IBB_INT_LATCHED_CLR_VREG); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_IBB_INT_LATCHED_CLR, rc); + goto exit; + } + +exit: + return rc; +} +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + static int qpnp_ibb_set_mode(struct qpnp_labibb *labibb, enum ibb_mode mode) { int rc; @@ -1528,6 +1954,9 @@ static int qpnp_lab_dt_init(struct qpnp_labibb *labibb, if (of_property_read_bool(of_node, "qcom,qpnp-lab-limit-max-current-enable")) { val = LAB_CURRENT_LIMIT_EN_BIT; +#ifdef SOMC_LABIBB_REGULATOR_ORG_IMPL + val |= LAB_CURRENT_LIMIT_OVERRIDE; +#endif /* SOMC_LABIBB_REGULATOR_ORG_IMPL */ rc = of_property_read_u32(of_node, "qcom,qpnp-lab-limit-maximum-current", &tmp); @@ -2313,6 +2742,7 @@ static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb) int dly; int retries; bool enabled = false; + u8 try_again =2; if (labibb->ttw_en && !labibb->ibb_vreg.vreg_enabled && labibb->in_ttw_mode) { @@ -2329,6 +2759,7 @@ static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb) return rc; } +TRY_AGN: /* total delay time */ dly = labibb->lab_vreg.soft_start + labibb->ibb_vreg.soft_start + labibb->ibb_vreg.pwrup_dly; @@ -2347,6 +2778,12 @@ 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_BIT)) && try_again) { + pr_err("LAB vreg ok not UP ! checking again"); + try_again--; + goto TRY_AGN; + } + if (!(val & LAB_STATUS1_VREG_OK_BIT)) { pr_err("failed for LAB %x\n", val); goto err_out; @@ -2378,6 +2815,9 @@ static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb) labibb->lab_vreg.vreg_enabled = 1; labibb->ibb_vreg.vreg_enabled = 1; +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + qpnp_labibb_interrupt_enable_ctl(labibb, VREG_WORKER_PASSIVE); +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ return 0; err_out: @@ -2411,8 +2851,15 @@ static int qpnp_labibb_regulator_disable(struct qpnp_labibb *labibb) pr_err("Error in entering TTW mode rc = %d\n", rc); return rc; } +#ifdef CONFIG_DRM_SDE_SPECIFIC_PANEL + if (labibb->lab_vreg.rdev->use_count == 1) + labibb->lab_vreg.vreg_enabled = 0; + if (labibb->ibb_vreg.rdev->use_count == 1) + labibb->ibb_vreg.vreg_enabled = 0; +#else labibb->lab_vreg.vreg_enabled = 0; labibb->ibb_vreg.vreg_enabled = 0; +#endif /* CONFIG_DRM_SDE_SPECIFIC_PANEL */ return 0; } @@ -2454,9 +2901,15 @@ static int qpnp_labibb_regulator_disable(struct qpnp_labibb *labibb) return rc; } } - +#ifdef CONFIG_DRM_SDE_SPECIFIC_PANEL + if (labibb->lab_vreg.rdev->use_count == 1) + labibb->lab_vreg.vreg_enabled = 0; + if (labibb->ibb_vreg.rdev->use_count == 1) + labibb->ibb_vreg.vreg_enabled = 0; +#else labibb->lab_vreg.vreg_enabled = 0; labibb->ibb_vreg.vreg_enabled = 0; +#endif /* CONFIG_DRM_SDE_SPECIFIC_PANEL */ return 0; } @@ -2492,6 +2945,9 @@ static int qpnp_lab_regulator_enable(struct regulator_dev *rdev) return rc; } labibb->lab_vreg.vreg_enabled = 1; +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + qpnp_lab_interrupt_enable_ctl(labibb, VREG_WORKER_PASSIVE); +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ } if (labibb->notify_lab_vreg_ok_sts || labibb->detect_lab_sc) @@ -2509,6 +2965,10 @@ static int qpnp_lab_regulator_disable(struct regulator_dev *rdev) if (labibb->secure_mode) return 0; +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + qpnp_lab_interrupt_disable_ctl(labibb); +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + if (labibb->lab_vreg.vreg_enabled && !labibb->swire_control) { if (!labibb->standalone) @@ -2522,8 +2982,12 @@ static int qpnp_lab_regulator_disable(struct regulator_dev *rdev) REG_LAB_ENABLE_CTL, rc); return rc; } - +#ifdef CONFIG_DRM_SDE_SPECIFIC_PANEL + if (rdev->use_count == 1) + labibb->lab_vreg.vreg_enabled = 0; +#else labibb->lab_vreg.vreg_enabled = 0; +#endif /* CONFIG_DRM_SDE_SPECIFIC_PANEL */ } return 0; } @@ -3078,6 +3542,26 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb, } labibb->lab_vreg.vreg_enabled = 1; +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + /* request interrupt */ + rc = qpnp_lab_request_interrupt(labibb); + if (rc) { + pr_err("lab request interrupt failed rc=%d\n", + rc); + return rc; + } + + rc = devm_request_threaded_irq(labibb->dev, + labibb->lab_vreg_irq, NULL, + lab_vreg_handler, + IRQF_LAB_FLAGS, + "lab_vreg_not_ok_interrupt", labibb); + if (rc) { + pr_err("Failed to register 'lab_vreg_not_ok_interrupt' irq rc=%d\n", + rc); + return rc; + } +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ } if (is_lab_vreg_ok_irq_available(labibb)) { @@ -3158,6 +3642,9 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb, return rc; } +#ifdef CONFIG_DRM_SDE_SPECIFIC_PANEL + labibb->lab_vreg.rdev->use_count = 1; +#endif /* CONFIG_DRM_SDE_SPECIFIC_PANEL */ } else { dev_err(labibb->dev, "qpnp lab regulator name missing\n"); return -EINVAL; @@ -3166,6 +3653,119 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb, return 0; } +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +static void force_labibb_regulator_disable(struct qpnp_labibb *labibb) +{ + int rc; + u8 val; + + val = 0; + rc = qpnp_labibb_write(labibb, + labibb->ibb_base + REG_IBB_ENABLE_CTL, &val, 1); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_IBB_ENABLE_CTL, rc); + goto exit; + } + + if (labibb->mode != QPNP_LABIBB_STANDALONE_MODE) + goto exit; + + val = 0; + rc = qpnp_labibb_write(labibb, + labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1); + if (rc) { + pr_err("%s: write register %x failed rc = %d\n", + __func__, REG_LAB_ENABLE_CTL, rc); + goto exit; + } +exit: + return; +} + +static void vreg_check_worker(struct work_struct *work) +{ + u8 val; + int rc; + struct qpnp_labibb *labibb = labibb_vreg_check.labibb; + + if ((!labibb->lab_vreg.vreg_enabled) || + (!labibb->ibb_vreg.vreg_enabled)) { + pr_debug("%s: false detection display is off\n", __func__); + goto exit; + } + + /* lab vreg status check */ + rc = qpnp_labibb_read(labibb, + labibb->lab_base + REG_LAB_STATUS1, + &val, + 1); + if (rc) { + pr_err("%s: read register %x failed rc = %d\n", + __func__, REG_LAB_STATUS1, rc); + goto read_error; + } + + if (!(val & LAB_STATUS1_VREG_OK_BIT)) + goto status_error; + + /* ibb vreg status check */ + rc = qpnp_labibb_read(labibb, + labibb->ibb_base + REG_IBB_STATUS1, + &val, + 1); + if (rc) { + pr_err("%s: read register %x failed rc = %d\n", + __func__, REG_IBB_STATUS1, rc); + goto read_error; + } + + if (!(val & IBB_STATUS1_VREG_OK_BIT)) + goto status_error; + + rc = qpnp_labibb_interrupt_enable_ctl(labibb, VREG_WORKER_ACTIVE); + if (rc) { + pr_err("%s: qpnp_labibb_interrupt_enable_ctl error\n", + __func__); + goto write_error; + } + + pr_debug("%s: vreg_check_worker done.\n", __func__); + goto exit; + +status_error: + labibb_vreg_check.current_chatter_cnt++; + pr_err("%s: VREG_NG Detection [%d]\n", + __func__, labibb_vreg_check.current_chatter_cnt); + if (labibb_vreg_check.current_chatter_cnt >= + labibb_vreg_check.target_chatter_cnt) { + pr_err("%s: execute shutdown.\n", __func__); + + /* disable vreg */ + force_labibb_regulator_disable(labibb); + /* shutdown */ + do { + pm_power_off(); + msleep(POWER_OFF_RETRY_INTERVAL); + } while (1); + goto exit; + } + +read_error: +write_error: + schedule_delayed_work(&labibb_vreg_check.vreg_check_work, + msecs_to_jiffies( + labibb_vreg_check.target_chatter_check_interval)); + return; + +exit: + /* initialize */ + labibb_vreg_check.current_chatter_cnt = 0; + labibb_vreg_check.vreg_check_working = false; + return; +} +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + static int qpnp_ibb_pfm_mode_enable(struct qpnp_labibb *labibb, struct device_node *of_node) { @@ -3315,6 +3915,253 @@ static bool qpnp_ibb_poff_ctl_required(struct qpnp_labibb *labibb) return true; } +#ifdef SOMC_LABIBB_REGULATOR_ORG_IMPL +/** This API is used to set precharge of LAB regulator + * regulator: the reglator device + * time: precharge time + * en: precharge control enable or not + */ +int qpnp_lab_set_precharge(struct regulator *regulator, u32 time, bool en) +{ + struct qpnp_labibb *labibb; + u8 val; + int rc; + + labibb = regulator_get_drvdata(regulator); + + for (val = 0; val < ARRAY_SIZE(lab_max_precharge_table); val++) + if (lab_max_precharge_table[val] == time) + break; + + if (val == ARRAY_SIZE(lab_max_precharge_table)) + val = ARRAY_SIZE(lab_max_precharge_table) - 1; + + if (en) + val |= LAB_FAST_PRECHARGE_CTL_EN; + + pr_debug("write base=0x%x val=0x%x\n", + (labibb->lab_base + REG_LAB_PRECHARGE_CTL), val); + + mutex_lock(&(labibb->lab_vreg.lab_mutex)); + + rc = qpnp_labibb_write(labibb, labibb->lab_base + + REG_LAB_PRECHARGE_CTL, &val, 1); + + mutex_unlock(&(labibb->lab_vreg.lab_mutex)); + return rc; +} +EXPORT_SYMBOL(qpnp_lab_set_precharge); + +/** This API is used to set soft-start of LAB regulator + * regulator: the reglator device + * time: soft start time + */ +int qpnp_lab_set_soft_start(struct regulator *regulator, u32 time) +{ + struct qpnp_labibb *labibb; + u8 val; + int rc; + + labibb = regulator_get_drvdata(regulator); + + for (val = 0; val < sizeof(ARRAY_SIZE(lab_soft_start_table)); val++) + if (lab_soft_start_table[val] == time) + break; + + if (val == ARRAY_SIZE(lab_soft_start_table)) + val = ARRAY_SIZE(lab_soft_start_table) - 1; + + pr_debug("write base=0x%x val=0x%x\n", + (labibb->lab_base + REG_LAB_SOFT_START_CTL), val); + + mutex_lock(&(labibb->lab_vreg.lab_mutex)); + + rc = qpnp_labibb_write(labibb, labibb->lab_base + + REG_LAB_SOFT_START_CTL, &val, 1); + + mutex_unlock(&(labibb->lab_vreg.lab_mutex)); + return rc; +} +EXPORT_SYMBOL(qpnp_lab_set_soft_start); + +/** This API is used to set pull-down of LAB regulator + * regulator: the reglator device + * en: pull-down enable or not + * strength: strength pull-down + */ +int qpnp_lab_set_pull_down(struct regulator *regulator, u8 strength) +{ + struct qpnp_labibb *labibb; + u8 val; + int rc = 0; + + labibb = regulator_get_drvdata(regulator); + + if (strength > 0) + val = LAB_PD_CTL_STRONG_PULL; + else + val = 0; + + mutex_lock(&(labibb->lab_vreg.lab_mutex)); + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_PD_CTL, + LAB_PD_CTL_STRENGTH_MASK, + val); + mutex_unlock(&(labibb->lab_vreg.lab_mutex)); + + if (rc) + pr_err("qpnp_lab_set_pd_strength write register %x failed rc = %d\n", + REG_LAB_PD_CTL, rc); + + return rc; +} +EXPORT_SYMBOL(qpnp_lab_set_pull_down); + +/** This API is used to set current max of LAB regulator + * regulator: the reglator device + * limit: current max of LAB regulator + */ +int qpnp_lab_set_current_max(struct regulator *regulator, u32 limit) +{ + struct qpnp_labibb *labibb; + int rc = 0; + u8 reg; + + labibb = regulator_get_drvdata(regulator); + + for (reg = 0; reg < ARRAY_SIZE(lab_current_limit_table); reg++) + if (lab_current_limit_table[reg] == limit) + break; + + if (reg == ARRAY_SIZE(lab_current_limit_table)) + reg = ARRAY_SIZE(lab_current_limit_table) - 1; + + pr_debug("write base=0x%x val=0x%x\n", + (labibb->lab_base + REG_LAB_CURRENT_LIMIT), reg); + + mutex_lock(&(labibb->lab_vreg.lab_mutex)); + + rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + + REG_LAB_CURRENT_LIMIT, + LAB_CURRENT_LIMIT_MASK, + reg); + if (rc) + pr_err("%s write register %x failed rc = %d\n", + __func__, REG_LAB_CURRENT_LIMIT, rc); + + mutex_unlock(&(labibb->lab_vreg.lab_mutex)); + return rc; +} +EXPORT_SYMBOL(qpnp_lab_set_current_max); + +/** This API is used to set soft-start of IBB regulator + * regulator: the reglator device + * time: soft start time + */ +int qpnp_ibb_set_soft_start(struct regulator *regulator, u32 time) +{ + struct qpnp_labibb *labibb; + u8 val; + int rc; + + labibb = regulator_get_drvdata(regulator); + + for (val = 0; val < sizeof(ARRAY_SIZE(ibb_dischg_res_table)); + val++) + if (ibb_dischg_res_table[val] == time) + break; + + if (val == ARRAY_SIZE(ibb_dischg_res_table)) + val = ARRAY_SIZE(ibb_dischg_res_table) - 1; + + pr_debug("write base=0x%x val=0x%x\n", + (labibb->ibb_base + REG_IBB_SOFT_START_CTL), val); + + mutex_lock(&(labibb->ibb_vreg.ibb_mutex)); + + rc = qpnp_labibb_write(labibb, labibb->ibb_base + + REG_IBB_SOFT_START_CTL, &val, 1); + + mutex_unlock(&(labibb->ibb_vreg.ibb_mutex)); + return rc; +} +EXPORT_SYMBOL(qpnp_ibb_set_soft_start); + +/** This API is used to set pull-down of IBB regulator + * regulator: the reglator device + * en: pull-down enable or not + * strength: strength pull-down + */ +int qpnp_ibb_set_pull_down(struct regulator *regulator, u8 strength) +{ + struct qpnp_labibb *labibb; + u8 val; + int rc = 0; + + labibb = regulator_get_drvdata(regulator); + + if (strength > 0) + val = 0; + else + val = IBB_PD_CTL_HALF_STRENGTH; + + mutex_lock(&(labibb->ibb_vreg.ibb_mutex)); + rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base + + REG_IBB_PD_CTL, + IBB_PD_CTL_STRENGTH_MASK, + val); + mutex_unlock(&(labibb->ibb_vreg.ibb_mutex)); + + if (rc) + pr_err("qpnp_ibb_set_pd_strength write register %x failed rc = %d\n", + REG_IBB_PD_CTL, rc); + + return rc; +} +EXPORT_SYMBOL(qpnp_ibb_set_pull_down); + +/** This API is used to set current max of IBB regulator + * regulator: the reglator device + * limit: current max of IBB regulator + */ +int qpnp_ibb_set_current_max(struct regulator *regulator, u32 limit) +{ + struct qpnp_labibb *labibb; + int rc = 0; + u8 reg = 0; + + labibb = regulator_get_drvdata(regulator); + + reg = IBB_CURRENT_LIMIT_VALUE; + + if (!(ibb_current_limit_table[reg] == limit)) { + pr_err("%s value mismatch\n", __func__); + return rc; + } + + if (reg == ARRAY_SIZE(ibb_current_limit_table)) + reg = ARRAY_SIZE(ibb_current_limit_table) - 1; + + pr_debug("write base=0x%x val=0x%x\n", + (labibb->ibb_base + REG_IBB_CURRENT_LIMIT), reg); + + mutex_lock(&(labibb->ibb_vreg.ibb_mutex)); + + rc = qpnp_labibb_sec_masked_write(labibb, labibb->ibb_base, + REG_IBB_CURRENT_LIMIT, + IBB_CURRENT_LIMIT_MASK, + reg); + + if (rc) + pr_err("%s write register %x failed rc = %d\n", + __func__, REG_IBB_CURRENT_LIMIT, rc); + + mutex_unlock(&(labibb->ibb_vreg.ibb_mutex)); + return rc; +} +EXPORT_SYMBOL(qpnp_ibb_set_current_max); +#endif /* SOMC_LABIBB_REGULATOR_ORG_IMPL */ + static int qpnp_ibb_dt_init(struct qpnp_labibb *labibb, struct device_node *of_node) { @@ -3599,6 +4446,9 @@ static int qpnp_ibb_regulator_enable(struct regulator_dev *rdev) return rc; } labibb->ibb_vreg.vreg_enabled = 1; +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + qpnp_ibb_interrupt_enable_ctl(labibb, VREG_WORKER_PASSIVE); +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ } return 0; @@ -3612,6 +4462,10 @@ static int qpnp_ibb_regulator_disable(struct regulator_dev *rdev) if (labibb->secure_mode) return 0; +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + qpnp_ibb_interrupt_disable_ctl(labibb); +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + if (labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) { if (!labibb->standalone) @@ -3623,7 +4477,12 @@ static int qpnp_ibb_regulator_disable(struct regulator_dev *rdev) return rc; } +#ifdef CONFIG_DRM_SDE_SPECIFIC_PANEL + if (rdev->use_count == 1) + labibb->ibb_vreg.vreg_enabled = 0; +#else labibb->ibb_vreg.vreg_enabled = 0; +#endif /* CONFIG_DRM_SDE_SPECIFIC_PANEL */ } return 0; } @@ -3814,6 +4673,26 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb, labibb->ibb_vreg.pwrdn_dly = ibb_pwrdn_dly_table[index]; labibb->ibb_vreg.vreg_enabled = 1; +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + /* request interrupt */ + rc = qpnp_ibb_request_interrupt(labibb); + if (rc) { + pr_err("ibb request interrupt failed rc=%d\n", + rc); + return rc; + } + + rc = devm_request_threaded_irq(labibb->dev, + labibb->ibb_vreg_irq, NULL, + ibb_vreg_handler, + IRQF_IBB_FLAGS, + "ibb_vreg_not_ok_interrupt", labibb); + if (rc) { + pr_err("Failed to register 'ibb_vreg_not_ok_interrupt' irq rc=%d\n", + rc); + return rc; + } +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ } else { /* SWIRE_RDY and IBB_MODULE_EN not enabled */ rc = qpnp_ibb_dt_init(labibb, of_node); @@ -3950,6 +4829,9 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb, return rc; } +#ifdef CONFIG_DRM_SDE_SPECIFIC_PANEL + labibb->ibb_vreg.rdev->use_count = 1; +#endif /* CONFIG_DRM_SDE_SPECIFIC_PANEL */ } else { dev_err(labibb->dev, "qpnp ibb regulator name missing\n"); return -EINVAL; @@ -3979,6 +4861,15 @@ static int qpnp_lab_register_irq(struct device_node *child, else labibb->lab_vreg.lab_sc_irq = rc; +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + labibb->lab_vreg_irq = + of_irq_get_byname(child, "lab_vreg_not_ok_interrupt"); + if (!labibb->lab_vreg_irq) { + pr_err("Invalid lab_vreg_not_ok_interrupt irq\n"); + return -EINVAL; + } +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + return 0; } @@ -3993,6 +4884,14 @@ static int qpnp_ibb_register_irq(struct device_node *child, pr_debug("Unable to get ibb-sc-err, rc = %d\n", rc); else labibb->ibb_vreg.ibb_sc_irq = rc; +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + labibb->ibb_vreg_irq = + of_irq_get_byname(child, "ibb_vreg_not_ok_interrupt"); + if (!labibb->ibb_vreg_irq) { + pr_err("Invalid ibb_vreg_not_ok_interrupt irq\n"); + return -EINVAL; + } +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ return 0; } @@ -4080,6 +4979,109 @@ static struct class_attribute labibb_attributes[] = { __ATTR_NULL, }; +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED +static irqreturn_t lab_vreg_handler(int irq, void *_chip) +{ + u8 val; + int rc; + struct qpnp_labibb *labibb = _chip; + + pr_err("%s: LAB VREG_NG interrupt!\n", __func__); + qpnp_labibb_interrupt_disable_ctl(labibb); + + if (!labibb->lab_vreg.vreg_enabled) { + pr_err("%s: false detection display is off\n", __func__); + goto exit; + } + + /* lab status1 */ + rc = qpnp_labibb_read(labibb, + labibb->lab_base + REG_LAB_STATUS1, + &val, + 1); + if (rc) { + pr_err("%s: read register %x failed rc = %d\n", + __func__, REG_LAB_STATUS1, rc); + goto exit; + } + + if (!(val & LAB_STATUS1_VREG_OK_BIT)) + pr_err("%s: LAB VREG NG!!!\n", __func__); + else + goto false_detection; + + /* start vreg check */ + if (labibb_vreg_check.vreg_check_working) + goto exit; + + labibb_vreg_check.vreg_check_working = true; + labibb_vreg_check.current_chatter_cnt = CHATTER_CNT_START; + schedule_delayed_work(&labibb_vreg_check.vreg_check_work, + msecs_to_jiffies( + labibb_vreg_check.target_chatter_check_interval)); + goto exit; + +false_detection: + rc = qpnp_labibb_interrupt_enable_ctl(labibb, VREG_WORKER_PASSIVE); + if (rc) + pr_err("%s: qpnp_labibb_interrupt_enable_ctl error\n", + __func__); +exit: + return IRQ_HANDLED; +} + +static irqreturn_t ibb_vreg_handler(int irq, void *_chip) +{ + u8 val; + int rc; + struct qpnp_labibb *labibb = _chip; + + pr_err("%s: IBB VREG_NG interrupt!\n", __func__); + qpnp_labibb_interrupt_disable_ctl(labibb); + + if (!labibb->ibb_vreg.vreg_enabled) { + pr_err("%s: false detection display is off\n", __func__); + goto exit; + } + + /* ibb status1 */ + rc = qpnp_labibb_read(labibb, + labibb->ibb_base + REG_IBB_STATUS1, + &val, + 1); + if (rc) { + pr_err("%s: read register %x failed rc = %d\n", + __func__, REG_IBB_STATUS1, rc); + goto exit; + } + + if (!(val & IBB_STATUS1_VREG_OK_BIT)) + pr_err("%s: IBB VREG NG!!!\n", __func__); + else + goto false_detection; + + /* start vreg check */ + if (labibb_vreg_check.vreg_check_working) + goto exit; + + labibb_vreg_check.vreg_check_working = true; + labibb_vreg_check.current_chatter_cnt = CHATTER_CNT_START; + schedule_delayed_work(&labibb_vreg_check.vreg_check_work, + msecs_to_jiffies( + labibb_vreg_check.target_chatter_check_interval)); + goto exit; + +false_detection: + rc = qpnp_labibb_interrupt_enable_ctl(labibb, VREG_WORKER_PASSIVE); + if (rc) + pr_err("%s: qpnp_labibb_interrupt_enable_ctl error\n", + __func__); + +exit: + return IRQ_HANDLED; +} +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + static int qpnp_labibb_regulator_probe(struct platform_device *pdev) { struct qpnp_labibb *labibb; @@ -4153,6 +5155,31 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev) } } +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + /* initialize labibb_vreg_status_ctrl */ + labibb_vreg_check.labibb = labibb; + labibb_vreg_check.current_chatter_cnt = 0; + labibb_vreg_check.vreg_check_working = false; + labibb_vreg_check.target_chatter_cnt = DEFAULT_TARGET_CHATTER_CNT; + rc = of_property_read_u32(labibb->dev->of_node, + "somc,vreg-target-chatter-cnt", + &(labibb_vreg_check.target_chatter_cnt)); + if (rc) + pr_info("qpnp_labibb: Target chatter count sets default.\n"); + + labibb_vreg_check.target_chatter_check_interval + = DEFAULT_TARGET_CHATTER_INTERVAL; + rc = of_property_read_u32(labibb->dev->of_node, + "somc,vreg-target-chatter-interval", + &(labibb_vreg_check.target_chatter_check_interval)); + if (rc) + pr_info("qpnp_labibb: Target chatter interval sets default.\n"); + + /* initialize vreg_check_worker */ + INIT_DELAYED_WORK(&labibb_vreg_check.vreg_check_work, + vreg_check_worker); +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ + labibb->standalone = of_property_read_bool(labibb->dev->of_node, "qcom,labibb-standalone"); @@ -4243,6 +5270,14 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev) labibb->ibb_base = base; labibb->ibb_dig_major = revision; qpnp_ibb_register_irq(child, labibb); +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + rc = qpnp_ibb_register_irq(child, labibb); + if (rc) { + pr_err("Failed to register IBB IRQ rc=%d\n", + rc); + goto fail_registration; + } +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ rc = register_qpnp_ibb_regulator(labibb, child); if (rc < 0) goto fail_registration; @@ -4295,7 +5330,12 @@ fail_registration: regulator_unregister(labibb->lab_vreg.rdev); if (labibb->ibb_vreg.rdev) regulator_unregister(labibb->ibb_vreg.rdev); - +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + if (labibb->lab_vreg_irq) + free_irq(labibb->lab_vreg_irq, labibb); + if (labibb->ibb_vreg_irq) + free_irq(labibb->ibb_vreg_irq, labibb); +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ return rc; } @@ -4322,6 +5362,13 @@ static int qpnp_labibb_regulator_remove(struct platform_device *pdev) regulator_unregister(labibb->ibb_vreg.rdev); cancel_work_sync(&labibb->lab_vreg_ok_work); +#ifdef CONFIG_SOMC_LCD_OCP_ENABLED + if (labibb->lab_vreg_irq) + free_irq(labibb->lab_vreg_irq, labibb); + if (labibb->ibb_vreg_irq) + free_irq(labibb->ibb_vreg_irq, labibb); + cancel_delayed_work_sync(&labibb_vreg_check.vreg_check_work); +#endif /* CONFIG_SOMC_LCD_OCP_ENABLED */ } return 0; } diff --git a/drivers/regulator/qpnp-regulator.c b/drivers/regulator/qpnp-regulator.c index aec3656eb9f0..e14b0a8851c0 100644 --- a/drivers/regulator/qpnp-regulator.c +++ b/drivers/regulator/qpnp-regulator.c @@ -341,6 +341,8 @@ struct qpnp_regulator { int ocp_count; int ocp_max_retries; int ocp_retry_delay_ms; + struct regulator_ocp_notification ocp_notification; + spinlock_t ocp_lock; int system_load; int hpm_min_load; int slew_rate; @@ -1246,6 +1248,34 @@ static int qpnp_regulator_common_enable_time(struct regulator_dev *rdev) return vreg->enable_time; } +static int qpnp_regulator_vs_register_ocp_notification( + struct regulator_dev *rdev, + struct regulator_ocp_notification *notification) +{ + unsigned long flags; + struct qpnp_regulator *vreg = rdev_get_drvdata(rdev); + + spin_lock_irqsave(&vreg->ocp_lock, flags); + if (notification) { + /* register ocp notification */ + vreg->ocp_notification = *notification; + } else { + /* unregister ocp notification */ + memset(&vreg->ocp_notification, 0, + sizeof(vreg->ocp_notification)); + } + spin_unlock_irqrestore(&vreg->ocp_lock, flags); + + if (qpnp_vreg_debug_mask & QPNP_VREG_DEBUG_OCP) { + pr_info("%s: registered ocp notification(notify=%p, ctxt=%p)\n", + vreg->rdesc.name, + vreg->ocp_notification.notify, + vreg->ocp_notification.ctxt); + } + + return 0; +} + static int qpnp_regulator_vs_clear_ocp(struct qpnp_regulator *vreg) { int rc; @@ -1319,8 +1349,14 @@ static irqreturn_t qpnp_regulator_vs_ocp_isr(int irq, void *data) schedule_delayed_work(&vreg->ocp_work, msecs_to_jiffies(vreg->ocp_retry_delay_ms) + 1); } else { + unsigned long flags; vreg_err(vreg, "OCP triggered %d times; no further retries\n", vreg->ocp_count); + spin_lock_irqsave(&vreg->ocp_lock, flags); + if (vreg->ocp_notification.notify) + vreg->ocp_notification.notify( + vreg->ocp_notification.ctxt); + spin_unlock_irqrestore(&vreg->ocp_lock, flags); } return IRQ_HANDLED; @@ -1551,6 +1587,8 @@ static struct regulator_ops qpnp_vs_ops = { .disable = qpnp_regulator_common_disable, .is_enabled = qpnp_regulator_common_is_enabled, .enable_time = qpnp_regulator_common_enable_time, + .register_ocp_notification + = qpnp_regulator_vs_register_ocp_notification, }; static struct regulator_ops qpnp_boost_ops = { @@ -2245,6 +2283,10 @@ static int qpnp_regulator_probe(struct platform_device *pdev) if (vreg->ocp_retry_delay_ms == 0) vreg->ocp_retry_delay_ms = QPNP_VS_OCP_DEFAULT_RETRY_DELAY_MS; + memset(&vreg->ocp_notification, 0, + sizeof(vreg->ocp_notification)); + spin_lock_init(&vreg->ocp_lock); + rdesc = &vreg->rdesc; rdesc->id = to_spmi_device(pdev->dev.parent)->ctrl->nr; rdesc->owner = THIS_MODULE;