Loading Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt +17 −5 Original line number Diff line number Diff line Loading @@ -88,11 +88,11 @@ LAB subnode required properties: 50, 60, 70 and 80. - interrupts: Specify the interrupts as per the interrupt encoding. Currently "lab-vreg-ok" is required for LCD mode in pmi8998. For AMOLED mode, "lab-vreg-ok" is required only when SWIRE control is enabled and skipping 2nd SWIRE pulse is required in pmi8952/8996. Currently "lab-vreg-ok" is required and "lab-sc_err" is optional for LCD mode in pmi8998. For AMOLED mode, "lab-vreg-ok" is required only when SWIRE control is enabled and skipping 2nd SWIRE pulse is required in pmi8952/8996. - interrupt-names: Interrupt names to match up 1-to-1 with the interrupts specified in 'interrupts' property. Loading Loading @@ -153,6 +153,10 @@ LAB subnode optional properties: any value in the allowed limit. - qcom,notify-lab-vreg-ok-sts: A boolean property which upon set will poll and notify the lab_vreg_ok status. - qcom,qpnp-lab-sc-wait-time-ms: This property is used to specify the time (in ms) to poll for the short circuit detection. If not specified the default time is 5 sec. Following properties are available only for PM660A: Loading Loading @@ -209,6 +213,14 @@ IBB subnode required properties: IBB subnode optional properties: - interrupts: Specify the interrupts as per the interrupt encoding. Currently "ibb-sc-err" could be used for LCD mode in pmi8998 to detect the short circuit fault. - interrupt-names: Interrupt names to match up 1-to-1 with the interrupts specified in 'interrupts' property. - qcom,qpnp-ibb-discharge-resistor: The discharge resistor in Kilo Ohms which controls the soft start time. Supported values are 300, 64, 32 and 16. Loading Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt +6 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,11 @@ Required Node Structure Value type: <string> Definition: should be "qcom,qpnp-oledb-regulator". - qcom,pmic-revid Usage: required Value type: <phandle> Definition: Used to identify the PMIC subtype. - reg Usage: required Value type: <prop-encoded-array> Loading Loading @@ -224,6 +229,7 @@ pm660a_oledb: qpnp-oledb@e000 { compatible = "qcom,qpnp-oledb-regulator"; #address-cells = <1>; #size-cells = <1>; qcom,pmic-revid = <&pm660l_revid>; reg = <0xe000 0x100>; label = "oledb"; Loading arch/arm64/boot/dts/qcom/pmi8998.dtsi +8 −1 Original line number Diff line number Diff line Loading @@ -483,6 +483,10 @@ regulator-min-microvolt = <4600000>; regulator-max-microvolt = <6000000>; interrupts = <0x3 0xdc 0x2 IRQ_TYPE_EDGE_RISING>; interrupt-names = "ibb-sc-err"; qcom,qpnp-ibb-min-voltage = <1400000>; qcom,qpnp-ibb-step-size = <100000>; qcom,qpnp-ibb-slew-rate = <2000000>; Loading Loading @@ -516,8 +520,11 @@ regulator-max-microvolt = <6000000>; interrupts = <0x3 0xde 0x0 IRQ_TYPE_EDGE_RISING>, <0x3 0xde 0x1 IRQ_TYPE_EDGE_RISING>; interrupt-names = "lab-vreg-ok"; interrupt-names = "lab-vreg-ok", "lab-sc-err"; qcom,qpnp-lab-min-voltage = <4600000>; qcom,qpnp-lab-step-size = <100000>; qcom,qpnp-lab-slew-rate = <5000>; Loading drivers/regulator/qpnp-labibb-regulator.c +356 −71 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/ktime.h> #include <linux/regmap.h> #include <linux/module.h> #include <linux/notifier.h> Loading @@ -37,6 +38,7 @@ #define REG_REVISION_2 0x01 #define REG_PERPH_TYPE 0x04 #define REG_INT_RT_STS 0x10 #define QPNP_LAB_TYPE 0x24 #define QPNP_IBB_TYPE 0x20 Loading Loading @@ -77,8 +79,8 @@ /* LAB register bits definitions */ /* REG_LAB_STATUS1 */ #define LAB_STATUS1_VREG_OK_MASK BIT(7) #define LAB_STATUS1_VREG_OK BIT(7) #define LAB_STATUS1_VREG_OK_BIT BIT(7) #define LAB_STATUS1_SC_DETECT_BIT BIT(6) /* REG_LAB_SWIRE_PGM_CTL */ #define LAB_EN_SWIRE_PGM_VOUT BIT(7) Loading Loading @@ -188,8 +190,8 @@ /* IBB register bits definition */ /* REG_IBB_STATUS1 */ #define IBB_STATUS1_VREG_OK_MASK BIT(7) #define IBB_STATUS1_VREG_OK BIT(7) #define IBB_STATUS1_VREG_OK_BIT BIT(7) #define IBB_STATUS1_SC_DETECT_BIT BIT(6) /* REG_IBB_VOLTAGE */ #define IBB_VOLTAGE_OVERRIDE_EN BIT(7) Loading Loading @@ -557,12 +559,15 @@ struct lab_regulator { struct mutex lab_mutex; int lab_vreg_ok_irq; int lab_sc_irq; int curr_volt; int min_volt; int step_size; int slew_rate; int soft_start; int sc_wait_time_ms; int vreg_enabled; }; Loading @@ -572,6 +577,8 @@ struct ibb_regulator { struct regulator_dev *rdev; struct mutex ibb_mutex; int ibb_sc_irq; int curr_volt; int min_volt; Loading Loading @@ -602,6 +609,9 @@ struct qpnp_labibb { struct mutex bus_mutex; enum qpnp_labibb_mode mode; struct work_struct lab_vreg_ok_work; struct delayed_work sc_err_recovery_work; struct hrtimer sc_err_check_timer; int sc_err_count; bool standalone; bool ttw_en; bool in_ttw_mode; Loading @@ -612,6 +622,8 @@ struct qpnp_labibb { bool skip_2nd_swire_cmd; bool pfm_enable; bool notify_lab_vreg_ok_sts; bool detect_lab_sc; bool sc_detected; u32 swire_2nd_cmd_delay; u32 swire_ibb_ps_enable_delay; }; Loading Loading @@ -2178,8 +2190,10 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work) u8 val; struct qpnp_labibb *labibb = container_of(work, struct qpnp_labibb, lab_vreg_ok_work); if (labibb->lab_vreg.sc_wait_time_ms != -EINVAL) retries = labibb->lab_vreg.sc_wait_time_ms / 5; while (retries--) { while (retries) { rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_STATUS1, &val, 1); if (rc < 0) { Loading @@ -2188,18 +2202,106 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work) return; } if (val & LAB_STATUS1_VREG_OK) { if (val & LAB_STATUS1_VREG_OK_BIT) { raw_notifier_call_chain(&labibb_notifier, LAB_VREG_OK, NULL); break; } usleep_range(dly, dly + 100); retries--; } if (!retries) if (!retries) { if (labibb->detect_lab_sc) { pr_crit("short circuit detected on LAB rail.. disabling the LAB/IBB/OLEDB modules\n"); /* Disable LAB module */ val = 0; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_MODULE_RDY, &val, 1); if (rc < 0) { pr_err("write register %x failed rc = %d\n", REG_LAB_MODULE_RDY, rc); return; } raw_notifier_call_chain(&labibb_notifier, LAB_VREG_NOT_OK, NULL); labibb->sc_detected = true; labibb->lab_vreg.vreg_enabled = 0; labibb->ibb_vreg.vreg_enabled = 0; } else { pr_err("LAB_VREG_OK not set, failed to notify\n"); } } } static int qpnp_lab_enable_standalone(struct qpnp_labibb *labibb) { int rc; u8 val; val = LAB_ENABLE_CTL_EN; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1); if (rc < 0) { pr_err("Write register %x failed rc = %d\n", REG_LAB_ENABLE_CTL, rc); return rc; } udelay(labibb->lab_vreg.soft_start); rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_STATUS1, &val, 1); if (rc < 0) { pr_err("Read register %x failed rc = %d\n", REG_LAB_STATUS1, rc); return rc; } if (!(val & LAB_STATUS1_VREG_OK_BIT)) { pr_err("Can't enable LAB standalone\n"); return -EINVAL; } return 0; } static int qpnp_ibb_enable_standalone(struct qpnp_labibb *labibb) { int rc, delay, retries = 10; u8 val; rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN); if (rc < 0) { pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc); return rc; } delay = labibb->ibb_vreg.soft_start; while (retries--) { /* Wait for a small period before reading IBB_STATUS1 */ usleep_range(delay, delay + 100); rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_STATUS1, &val, 1); if (rc < 0) { pr_err("Read register %x failed rc = %d\n", REG_IBB_STATUS1, rc); return rc; } if (val & IBB_STATUS1_VREG_OK_BIT) break; } if (!(val & IBB_STATUS1_VREG_OK_BIT)) { pr_err("Can't enable IBB standalone\n"); return -EINVAL; } return 0; } static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb) { Loading Loading @@ -2242,7 +2344,7 @@ static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb) labibb->lab_vreg.soft_start, labibb->ibb_vreg.soft_start, labibb->ibb_vreg.pwrup_dly, dly); if (!(val & LAB_STATUS1_VREG_OK)) { if (!(val & LAB_STATUS1_VREG_OK_BIT)) { pr_err("failed for LAB %x\n", val); goto err_out; } Loading @@ -2259,7 +2361,7 @@ static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb) goto err_out; } if (val & IBB_STATUS1_VREG_OK) { if (val & IBB_STATUS1_VREG_OK_BIT) { enabled = true; break; } Loading Loading @@ -2330,7 +2432,7 @@ static int qpnp_labibb_regulator_disable(struct qpnp_labibb *labibb) return rc; } if (!(val & IBB_STATUS1_VREG_OK)) { if (!(val & IBB_STATUS1_VREG_OK_BIT)) { disabled = true; break; } Loading Loading @@ -2359,10 +2461,13 @@ static int qpnp_labibb_regulator_disable(struct qpnp_labibb *labibb) static int qpnp_lab_regulator_enable(struct regulator_dev *rdev) { int rc; u8 val; struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); if (labibb->sc_detected) { pr_info("Short circuit detected: disabled LAB/IBB rails\n"); return 0; } if (labibb->skip_2nd_swire_cmd) { rc = qpnp_ibb_ps_config(labibb, false); if (rc < 0) { Loading @@ -2372,38 +2477,18 @@ static int qpnp_lab_regulator_enable(struct regulator_dev *rdev) } if (!labibb->lab_vreg.vreg_enabled && !labibb->swire_control) { if (!labibb->standalone) return qpnp_labibb_regulator_enable(labibb); val = LAB_ENABLE_CTL_EN; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1); if (rc < 0) { pr_err("qpnp_lab_regulator_enable write register %x failed rc = %d\n", REG_LAB_ENABLE_CTL, rc); return rc; } udelay(labibb->lab_vreg.soft_start); rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_STATUS1, &val, 1); if (rc < 0) { pr_err("qpnp_lab_regulator_enable read register %x failed rc = %d\n", REG_LAB_STATUS1, rc); rc = qpnp_lab_enable_standalone(labibb); if (rc) { pr_err("enable lab standalone failed, rc=%d\n", rc); return rc; } if ((val & LAB_STATUS1_VREG_OK_MASK) != LAB_STATUS1_VREG_OK) { pr_err("qpnp_lab_regulator_enable failed\n"); return -EINVAL; } labibb->lab_vreg.vreg_enabled = 1; } if (labibb->notify_lab_vreg_ok_sts) if (labibb->notify_lab_vreg_ok_sts || labibb->detect_lab_sc) schedule_work(&labibb->lab_vreg_ok_work); return 0; Loading Loading @@ -2444,6 +2529,163 @@ static int qpnp_lab_regulator_is_enabled(struct regulator_dev *rdev) return labibb->lab_vreg.vreg_enabled; } static int qpnp_labibb_force_enable(struct qpnp_labibb *labibb) { int rc; if (labibb->skip_2nd_swire_cmd) { rc = qpnp_ibb_ps_config(labibb, false); if (rc < 0) { pr_err("Failed to disable IBB PS rc=%d\n", rc); return rc; } } if (!labibb->swire_control) { if (!labibb->standalone) return qpnp_labibb_regulator_enable(labibb); rc = qpnp_ibb_enable_standalone(labibb); if (rc < 0) { pr_err("enable ibb standalone failed, rc=%d\n", rc); return rc; } labibb->ibb_vreg.vreg_enabled = 1; rc = qpnp_lab_enable_standalone(labibb); if (rc < 0) { pr_err("enable lab standalone failed, rc=%d\n", rc); return rc; } labibb->lab_vreg.vreg_enabled = 1; } return 0; } #define SC_ERR_RECOVERY_DELAY_MS 250 #define SC_ERR_COUNT_INTERVAL_SEC 1 #define POLLING_SCP_DONE_COUNT 2 #define POLLING_SCP_DONE_INTERVAL_MS 5 static irqreturn_t labibb_sc_err_handler(int irq, void *_labibb) { int rc; u16 reg; u8 sc_err_mask, val; char *str; struct qpnp_labibb *labibb = (struct qpnp_labibb *)_labibb; bool in_sc_err, lab_en, ibb_en, scp_done = false; int count; if (irq == labibb->lab_vreg.lab_sc_irq) { reg = labibb->lab_base + REG_LAB_STATUS1; sc_err_mask = LAB_STATUS1_SC_DETECT_BIT; str = "LAB"; } else if (irq == labibb->ibb_vreg.ibb_sc_irq) { reg = labibb->ibb_base + REG_IBB_STATUS1; sc_err_mask = IBB_STATUS1_SC_DETECT_BIT; str = "IBB"; } else { return IRQ_HANDLED; } rc = qpnp_labibb_read(labibb, reg, &val, 1); if (rc < 0) { pr_err("Read 0x%x failed, rc=%d\n", reg, rc); return IRQ_HANDLED; } pr_debug("%s SC error triggered! %s_STATUS1 = %d\n", str, str, val); in_sc_err = !!(val & sc_err_mask); /* * The SC fault would trigger PBS to disable regulators * for protection. This would cause the SC_DETECT status being * cleared so that it's not able to get the SC fault status. * Check if LAB/IBB regulators are enabled in the driver but * disabled in hardware, this means a SC fault had happened * and SCP handling is completed by PBS. */ if (!in_sc_err) { count = POLLING_SCP_DONE_COUNT; do { reg = labibb->lab_base + REG_LAB_ENABLE_CTL; rc = qpnp_labibb_read(labibb, reg, &val, 1); if (rc < 0) { pr_err("Read 0x%x failed, rc=%d\n", reg, rc); return IRQ_HANDLED; } lab_en = !!(val & LAB_ENABLE_CTL_EN); reg = labibb->ibb_base + REG_IBB_ENABLE_CTL; rc = qpnp_labibb_read(labibb, reg, &val, 1); if (rc < 0) { pr_err("Read 0x%x failed, rc=%d\n", reg, rc); return IRQ_HANDLED; } ibb_en = !!(val & IBB_ENABLE_CTL_MODULE_EN); if (lab_en || ibb_en) msleep(POLLING_SCP_DONE_INTERVAL_MS); else break; } while ((lab_en || ibb_en) && count--); if (labibb->lab_vreg.vreg_enabled && labibb->ibb_vreg.vreg_enabled && !lab_en && !ibb_en) { pr_debug("LAB/IBB has been disabled by SCP\n"); scp_done = true; } } if (in_sc_err || scp_done) { if (hrtimer_active(&labibb->sc_err_check_timer) || hrtimer_callback_running(&labibb->sc_err_check_timer)) { labibb->sc_err_count++; } else { labibb->sc_err_count = 1; hrtimer_start(&labibb->sc_err_check_timer, ktime_set(SC_ERR_COUNT_INTERVAL_SEC, 0), HRTIMER_MODE_REL); } schedule_delayed_work(&labibb->sc_err_recovery_work, msecs_to_jiffies(SC_ERR_RECOVERY_DELAY_MS)); } return IRQ_HANDLED; } #define SC_FAULT_COUNT_MAX 4 static enum hrtimer_restart labibb_check_sc_err_count(struct hrtimer *timer) { struct qpnp_labibb *labibb = container_of(timer, struct qpnp_labibb, sc_err_check_timer); /* * if SC fault triggers more than 4 times in 1 second, * then disable the IRQs and leave as it. */ if (labibb->sc_err_count > SC_FAULT_COUNT_MAX) { disable_irq(labibb->lab_vreg.lab_sc_irq); disable_irq(labibb->ibb_vreg.ibb_sc_irq); } return HRTIMER_NORESTART; } static void labibb_sc_err_recovery_work(struct work_struct *work) { struct qpnp_labibb *labibb = container_of(work, struct qpnp_labibb, sc_err_recovery_work.work); int rc; labibb->ibb_vreg.vreg_enabled = 0; labibb->lab_vreg.vreg_enabled = 0; rc = qpnp_labibb_force_enable(labibb); if (rc < 0) pr_err("force enable labibb failed, rc=%d\n", rc); } static int qpnp_lab_regulator_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned int *selector) { Loading Loading @@ -2505,7 +2747,7 @@ static int qpnp_skip_swire_command(struct qpnp_labibb *labibb) pr_err("Failed to read ibb_status1 reg rc=%d\n", rc); return rc; } if ((reg & IBB_STATUS1_VREG_OK_MASK) == IBB_STATUS1_VREG_OK) if (reg & IBB_STATUS1_VREG_OK_BIT) break; /* poll delay */ Loading Loading @@ -2661,6 +2903,12 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb, labibb->notify_lab_vreg_ok_sts = of_property_read_bool(of_node, "qcom,notify-lab-vreg-ok-sts"); labibb->lab_vreg.sc_wait_time_ms = -EINVAL; if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE && labibb->detect_lab_sc) of_property_read_u32(of_node, "qcom,qpnp-lab-sc-wait-time-ms", &labibb->lab_vreg.sc_wait_time_ms); rc = of_property_read_u32(of_node, "qcom,qpnp-lab-soft-start", &(labibb->lab_vreg.soft_start)); if (!rc) { Loading Loading @@ -2833,6 +3081,18 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb, } } if (labibb->lab_vreg.lab_sc_irq != -EINVAL) { rc = devm_request_threaded_irq(labibb->dev, labibb->lab_vreg.lab_sc_irq, NULL, labibb_sc_err_handler, IRQF_ONESHOT | IRQF_TRIGGER_RISING, "lab-sc-err", labibb); if (rc) { pr_err("Failed to register 'lab-sc-err' irq rc=%d\n", rc); return rc; } } rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_MODULE_RDY, &val, 1); if (rc < 0) { Loading Loading @@ -3302,45 +3562,26 @@ static int qpnp_ibb_dt_init(struct qpnp_labibb *labibb, static int qpnp_ibb_regulator_enable(struct regulator_dev *rdev) { int rc, delay, retries = 10; u8 val; int rc = 0; struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); if (!labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) { if (labibb->sc_detected) { pr_info("Short circuit detected: disabled LAB/IBB rails\n"); return 0; } if (!labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) { if (!labibb->standalone) return qpnp_labibb_regulator_enable(labibb); rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN); rc = qpnp_ibb_enable_standalone(labibb); if (rc < 0) { pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc); pr_err("enable ibb standalone failed, rc=%d\n", rc); return rc; } delay = labibb->ibb_vreg.soft_start; while (retries--) { /* Wait for a small period before reading IBB_STATUS1 */ usleep_range(delay, delay + 100); rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_STATUS1, &val, 1); if (rc < 0) { pr_err("qpnp_ibb_regulator_enable read register %x failed rc = %d\n", REG_IBB_STATUS1, rc); return rc; } if (val & IBB_STATUS1_VREG_OK) break; } if (!(val & IBB_STATUS1_VREG_OK)) { pr_err("qpnp_ibb_regulator_enable failed\n"); return -EINVAL; } labibb->ibb_vreg.vreg_enabled = 1; } return 0; } Loading Loading @@ -3389,7 +3630,6 @@ static int qpnp_ibb_regulator_set_voltage(struct regulator_dev *rdev, return rc; } static int qpnp_ibb_regulator_get_voltage(struct regulator_dev *rdev) { struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); Loading Loading @@ -3611,6 +3851,19 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb, labibb->ibb_vreg.pwrdn_dly = 0; } if (labibb->ibb_vreg.ibb_sc_irq != -EINVAL) { rc = devm_request_threaded_irq(labibb->dev, labibb->ibb_vreg.ibb_sc_irq, NULL, labibb_sc_err_handler, IRQF_ONESHOT | IRQF_TRIGGER_RISING, "ibb-sc-err", labibb); if (rc) { pr_err("Failed to register 'ibb-sc-err' irq rc=%d\n", rc); return rc; } } rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_MODULE_RDY, &val, 1); if (rc < 0) { Loading Loading @@ -3684,15 +3937,39 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb, static int qpnp_lab_register_irq(struct device_node *child, struct qpnp_labibb *labibb) { int rc = 0; if (is_lab_vreg_ok_irq_available(labibb)) { labibb->lab_vreg.lab_vreg_ok_irq = of_irq_get_byname(child, "lab-vreg-ok"); if (labibb->lab_vreg.lab_vreg_ok_irq < 0) { rc = of_irq_get_byname(child, "lab-vreg-ok"); if (rc < 0) { pr_err("Invalid lab-vreg-ok irq\n"); return -EINVAL; return rc; } labibb->lab_vreg.lab_vreg_ok_irq = rc; } labibb->lab_vreg.lab_sc_irq = -EINVAL; rc = of_irq_get_byname(child, "lab-sc-err"); if (rc < 0) pr_debug("Unable to get lab-sc-err, rc = %d\n", rc); else labibb->lab_vreg.lab_sc_irq = rc; return 0; } static int qpnp_ibb_register_irq(struct device_node *child, struct qpnp_labibb *labibb) { int rc; labibb->ibb_vreg.ibb_sc_irq = -EINVAL; rc = of_irq_get_byname(child, "ibb-sc-err"); if (rc < 0) pr_debug("Unable to get ibb-sc-err, rc = %d\n", rc); else labibb->ibb_vreg.ibb_sc_irq = rc; return 0; } Loading Loading @@ -3788,6 +4065,8 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev) if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) { labibb->mode = QPNP_LABIBB_AMOLED_MODE; /* Enable polling for LAB short circuit detection for PM660A */ labibb->detect_lab_sc = true; } else { rc = of_property_read_string(labibb->dev->of_node, "qcom,qpnp-labibb-mode", &mode_name); Loading Loading @@ -3896,6 +4175,7 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev) case QPNP_IBB_TYPE: labibb->ibb_base = base; labibb->ibb_dig_major = revision; qpnp_ibb_register_irq(child, labibb); rc = register_qpnp_ibb_regulator(labibb, child); if (rc < 0) goto fail_registration; Loading @@ -3919,6 +4199,11 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev) } INIT_WORK(&labibb->lab_vreg_ok_work, qpnp_lab_vreg_notifier_work); INIT_DELAYED_WORK(&labibb->sc_err_recovery_work, labibb_sc_err_recovery_work); hrtimer_init(&labibb->sc_err_check_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); labibb->sc_err_check_timer.function = labibb_check_sc_err_count; dev_set_drvdata(&pdev->dev, labibb); pr_info("LAB/IBB registered successfully, lab_vreg enable=%d ibb_vreg enable=%d swire_control=%d\n", labibb->lab_vreg.vreg_enabled, Loading drivers/regulator/qpnp-oledb-regulator.c +46 −2 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include <linux/regulator/of_regulator.h> #include <linux/regulator/qpnp-labibb-regulator.h> #include <linux/qpnp/qpnp-pbs.h> #include <linux/qpnp/qpnp-revid.h> #define QPNP_OLEDB_REGULATOR_DRIVER_NAME "qcom,qpnp-oledb-regulator" #define OLEDB_VOUT_STEP_MV 100 Loading Loading @@ -162,6 +163,7 @@ struct qpnp_oledb { struct notifier_block oledb_nb; struct mutex bus_lock; struct device_node *pbs_dev_node; struct pmic_revid_data *pmic_rev_id; u32 base; u8 mod_enable; Loading @@ -181,6 +183,8 @@ struct qpnp_oledb { bool dynamic_ext_pinctl_config; bool pbs_control; bool force_pd_control; bool handle_lab_sc_notification; bool lab_sc_detected; }; static const u16 oledb_warmup_dly_ns[] = {6700, 13300, 26700, 53400}; Loading Loading @@ -275,6 +279,11 @@ static int qpnp_oledb_regulator_enable(struct regulator_dev *rdev) struct qpnp_oledb *oledb = rdev_get_drvdata(rdev); if (oledb->lab_sc_detected == true) { pr_info("Short circuit detected: Disabled OLEDB rail\n"); return 0; } if (oledb->ext_pin_control) { rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_EXT_PIN_CTL, &val, 1); Loading Loading @@ -1085,8 +1094,22 @@ static int qpnp_oledb_parse_fast_precharge(struct qpnp_oledb *oledb) static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb) { int rc = 0; struct device_node *revid_dev_node; struct device_node *of_node = oledb->dev->of_node; revid_dev_node = of_parse_phandle(oledb->dev->of_node, "qcom,pmic-revid", 0); if (!revid_dev_node) { pr_err("Missing qcom,pmic-revid property - driver failed\n"); return -EINVAL; } oledb->pmic_rev_id = get_revid_data(revid_dev_node); if (IS_ERR(oledb->pmic_rev_id)) { pr_debug("Unable to get revid data\n"); return -EPROBE_DEFER; } oledb->swire_control = of_property_read_bool(of_node, "qcom,swire-control"); Loading Loading @@ -1227,15 +1250,32 @@ static int qpnp_labibb_notifier_cb(struct notifier_block *nb, unsigned long action, void *data) { int rc = 0; u8 val; struct qpnp_oledb *oledb = container_of(nb, struct qpnp_oledb, oledb_nb); if (action == LAB_VREG_NOT_OK) { /* short circuit detected. Disable OLEDB module */ val = 0; rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_MODULE_RDY, &val, 1); if (rc < 0) { pr_err("Failed to write MODULE_RDY rc=%d\n", rc); return NOTIFY_STOP; } oledb->lab_sc_detected = true; oledb->mod_enable = false; pr_crit("LAB SC detected, disabling OLEDB forever!\n"); } if (action == LAB_VREG_OK) { /* Disable SWIRE pull down control and enable via spmi mode */ rc = qpnp_oledb_force_pulldown_config(oledb); if (rc < 0) if (rc < 0) { pr_err("Failed to config force pull down\n"); return NOTIFY_STOP; } } return NOTIFY_OK; } Loading Loading @@ -1281,7 +1321,11 @@ static int qpnp_oledb_regulator_probe(struct platform_device *pdev) return rc; } if (oledb->force_pd_control) { /* Enable LAB short circuit notification support */ if (oledb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) oledb->handle_lab_sc_notification = true; if (oledb->force_pd_control || oledb->handle_lab_sc_notification) { oledb->oledb_nb.notifier_call = qpnp_labibb_notifier_cb; rc = qpnp_labibb_notifier_register(&oledb->oledb_nb); if (rc < 0) { Loading Loading
Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt +17 −5 Original line number Diff line number Diff line Loading @@ -88,11 +88,11 @@ LAB subnode required properties: 50, 60, 70 and 80. - interrupts: Specify the interrupts as per the interrupt encoding. Currently "lab-vreg-ok" is required for LCD mode in pmi8998. For AMOLED mode, "lab-vreg-ok" is required only when SWIRE control is enabled and skipping 2nd SWIRE pulse is required in pmi8952/8996. Currently "lab-vreg-ok" is required and "lab-sc_err" is optional for LCD mode in pmi8998. For AMOLED mode, "lab-vreg-ok" is required only when SWIRE control is enabled and skipping 2nd SWIRE pulse is required in pmi8952/8996. - interrupt-names: Interrupt names to match up 1-to-1 with the interrupts specified in 'interrupts' property. Loading Loading @@ -153,6 +153,10 @@ LAB subnode optional properties: any value in the allowed limit. - qcom,notify-lab-vreg-ok-sts: A boolean property which upon set will poll and notify the lab_vreg_ok status. - qcom,qpnp-lab-sc-wait-time-ms: This property is used to specify the time (in ms) to poll for the short circuit detection. If not specified the default time is 5 sec. Following properties are available only for PM660A: Loading Loading @@ -209,6 +213,14 @@ IBB subnode required properties: IBB subnode optional properties: - interrupts: Specify the interrupts as per the interrupt encoding. Currently "ibb-sc-err" could be used for LCD mode in pmi8998 to detect the short circuit fault. - interrupt-names: Interrupt names to match up 1-to-1 with the interrupts specified in 'interrupts' property. - qcom,qpnp-ibb-discharge-resistor: The discharge resistor in Kilo Ohms which controls the soft start time. Supported values are 300, 64, 32 and 16. Loading
Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt +6 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,11 @@ Required Node Structure Value type: <string> Definition: should be "qcom,qpnp-oledb-regulator". - qcom,pmic-revid Usage: required Value type: <phandle> Definition: Used to identify the PMIC subtype. - reg Usage: required Value type: <prop-encoded-array> Loading Loading @@ -224,6 +229,7 @@ pm660a_oledb: qpnp-oledb@e000 { compatible = "qcom,qpnp-oledb-regulator"; #address-cells = <1>; #size-cells = <1>; qcom,pmic-revid = <&pm660l_revid>; reg = <0xe000 0x100>; label = "oledb"; Loading
arch/arm64/boot/dts/qcom/pmi8998.dtsi +8 −1 Original line number Diff line number Diff line Loading @@ -483,6 +483,10 @@ regulator-min-microvolt = <4600000>; regulator-max-microvolt = <6000000>; interrupts = <0x3 0xdc 0x2 IRQ_TYPE_EDGE_RISING>; interrupt-names = "ibb-sc-err"; qcom,qpnp-ibb-min-voltage = <1400000>; qcom,qpnp-ibb-step-size = <100000>; qcom,qpnp-ibb-slew-rate = <2000000>; Loading Loading @@ -516,8 +520,11 @@ regulator-max-microvolt = <6000000>; interrupts = <0x3 0xde 0x0 IRQ_TYPE_EDGE_RISING>, <0x3 0xde 0x1 IRQ_TYPE_EDGE_RISING>; interrupt-names = "lab-vreg-ok"; interrupt-names = "lab-vreg-ok", "lab-sc-err"; qcom,qpnp-lab-min-voltage = <4600000>; qcom,qpnp-lab-step-size = <100000>; qcom,qpnp-lab-slew-rate = <5000>; Loading
drivers/regulator/qpnp-labibb-regulator.c +356 −71 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/ktime.h> #include <linux/regmap.h> #include <linux/module.h> #include <linux/notifier.h> Loading @@ -37,6 +38,7 @@ #define REG_REVISION_2 0x01 #define REG_PERPH_TYPE 0x04 #define REG_INT_RT_STS 0x10 #define QPNP_LAB_TYPE 0x24 #define QPNP_IBB_TYPE 0x20 Loading Loading @@ -77,8 +79,8 @@ /* LAB register bits definitions */ /* REG_LAB_STATUS1 */ #define LAB_STATUS1_VREG_OK_MASK BIT(7) #define LAB_STATUS1_VREG_OK BIT(7) #define LAB_STATUS1_VREG_OK_BIT BIT(7) #define LAB_STATUS1_SC_DETECT_BIT BIT(6) /* REG_LAB_SWIRE_PGM_CTL */ #define LAB_EN_SWIRE_PGM_VOUT BIT(7) Loading Loading @@ -188,8 +190,8 @@ /* IBB register bits definition */ /* REG_IBB_STATUS1 */ #define IBB_STATUS1_VREG_OK_MASK BIT(7) #define IBB_STATUS1_VREG_OK BIT(7) #define IBB_STATUS1_VREG_OK_BIT BIT(7) #define IBB_STATUS1_SC_DETECT_BIT BIT(6) /* REG_IBB_VOLTAGE */ #define IBB_VOLTAGE_OVERRIDE_EN BIT(7) Loading Loading @@ -557,12 +559,15 @@ struct lab_regulator { struct mutex lab_mutex; int lab_vreg_ok_irq; int lab_sc_irq; int curr_volt; int min_volt; int step_size; int slew_rate; int soft_start; int sc_wait_time_ms; int vreg_enabled; }; Loading @@ -572,6 +577,8 @@ struct ibb_regulator { struct regulator_dev *rdev; struct mutex ibb_mutex; int ibb_sc_irq; int curr_volt; int min_volt; Loading Loading @@ -602,6 +609,9 @@ struct qpnp_labibb { struct mutex bus_mutex; enum qpnp_labibb_mode mode; struct work_struct lab_vreg_ok_work; struct delayed_work sc_err_recovery_work; struct hrtimer sc_err_check_timer; int sc_err_count; bool standalone; bool ttw_en; bool in_ttw_mode; Loading @@ -612,6 +622,8 @@ struct qpnp_labibb { bool skip_2nd_swire_cmd; bool pfm_enable; bool notify_lab_vreg_ok_sts; bool detect_lab_sc; bool sc_detected; u32 swire_2nd_cmd_delay; u32 swire_ibb_ps_enable_delay; }; Loading Loading @@ -2178,8 +2190,10 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work) u8 val; struct qpnp_labibb *labibb = container_of(work, struct qpnp_labibb, lab_vreg_ok_work); if (labibb->lab_vreg.sc_wait_time_ms != -EINVAL) retries = labibb->lab_vreg.sc_wait_time_ms / 5; while (retries--) { while (retries) { rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_STATUS1, &val, 1); if (rc < 0) { Loading @@ -2188,18 +2202,106 @@ static void qpnp_lab_vreg_notifier_work(struct work_struct *work) return; } if (val & LAB_STATUS1_VREG_OK) { if (val & LAB_STATUS1_VREG_OK_BIT) { raw_notifier_call_chain(&labibb_notifier, LAB_VREG_OK, NULL); break; } usleep_range(dly, dly + 100); retries--; } if (!retries) if (!retries) { if (labibb->detect_lab_sc) { pr_crit("short circuit detected on LAB rail.. disabling the LAB/IBB/OLEDB modules\n"); /* Disable LAB module */ val = 0; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_MODULE_RDY, &val, 1); if (rc < 0) { pr_err("write register %x failed rc = %d\n", REG_LAB_MODULE_RDY, rc); return; } raw_notifier_call_chain(&labibb_notifier, LAB_VREG_NOT_OK, NULL); labibb->sc_detected = true; labibb->lab_vreg.vreg_enabled = 0; labibb->ibb_vreg.vreg_enabled = 0; } else { pr_err("LAB_VREG_OK not set, failed to notify\n"); } } } static int qpnp_lab_enable_standalone(struct qpnp_labibb *labibb) { int rc; u8 val; val = LAB_ENABLE_CTL_EN; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1); if (rc < 0) { pr_err("Write register %x failed rc = %d\n", REG_LAB_ENABLE_CTL, rc); return rc; } udelay(labibb->lab_vreg.soft_start); rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_STATUS1, &val, 1); if (rc < 0) { pr_err("Read register %x failed rc = %d\n", REG_LAB_STATUS1, rc); return rc; } if (!(val & LAB_STATUS1_VREG_OK_BIT)) { pr_err("Can't enable LAB standalone\n"); return -EINVAL; } return 0; } static int qpnp_ibb_enable_standalone(struct qpnp_labibb *labibb) { int rc, delay, retries = 10; u8 val; rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN); if (rc < 0) { pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc); return rc; } delay = labibb->ibb_vreg.soft_start; while (retries--) { /* Wait for a small period before reading IBB_STATUS1 */ usleep_range(delay, delay + 100); rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_STATUS1, &val, 1); if (rc < 0) { pr_err("Read register %x failed rc = %d\n", REG_IBB_STATUS1, rc); return rc; } if (val & IBB_STATUS1_VREG_OK_BIT) break; } if (!(val & IBB_STATUS1_VREG_OK_BIT)) { pr_err("Can't enable IBB standalone\n"); return -EINVAL; } return 0; } static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb) { Loading Loading @@ -2242,7 +2344,7 @@ static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb) labibb->lab_vreg.soft_start, labibb->ibb_vreg.soft_start, labibb->ibb_vreg.pwrup_dly, dly); if (!(val & LAB_STATUS1_VREG_OK)) { if (!(val & LAB_STATUS1_VREG_OK_BIT)) { pr_err("failed for LAB %x\n", val); goto err_out; } Loading @@ -2259,7 +2361,7 @@ static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb) goto err_out; } if (val & IBB_STATUS1_VREG_OK) { if (val & IBB_STATUS1_VREG_OK_BIT) { enabled = true; break; } Loading Loading @@ -2330,7 +2432,7 @@ static int qpnp_labibb_regulator_disable(struct qpnp_labibb *labibb) return rc; } if (!(val & IBB_STATUS1_VREG_OK)) { if (!(val & IBB_STATUS1_VREG_OK_BIT)) { disabled = true; break; } Loading Loading @@ -2359,10 +2461,13 @@ static int qpnp_labibb_regulator_disable(struct qpnp_labibb *labibb) static int qpnp_lab_regulator_enable(struct regulator_dev *rdev) { int rc; u8 val; struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); if (labibb->sc_detected) { pr_info("Short circuit detected: disabled LAB/IBB rails\n"); return 0; } if (labibb->skip_2nd_swire_cmd) { rc = qpnp_ibb_ps_config(labibb, false); if (rc < 0) { Loading @@ -2372,38 +2477,18 @@ static int qpnp_lab_regulator_enable(struct regulator_dev *rdev) } if (!labibb->lab_vreg.vreg_enabled && !labibb->swire_control) { if (!labibb->standalone) return qpnp_labibb_regulator_enable(labibb); val = LAB_ENABLE_CTL_EN; rc = qpnp_labibb_write(labibb, labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1); if (rc < 0) { pr_err("qpnp_lab_regulator_enable write register %x failed rc = %d\n", REG_LAB_ENABLE_CTL, rc); return rc; } udelay(labibb->lab_vreg.soft_start); rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_STATUS1, &val, 1); if (rc < 0) { pr_err("qpnp_lab_regulator_enable read register %x failed rc = %d\n", REG_LAB_STATUS1, rc); rc = qpnp_lab_enable_standalone(labibb); if (rc) { pr_err("enable lab standalone failed, rc=%d\n", rc); return rc; } if ((val & LAB_STATUS1_VREG_OK_MASK) != LAB_STATUS1_VREG_OK) { pr_err("qpnp_lab_regulator_enable failed\n"); return -EINVAL; } labibb->lab_vreg.vreg_enabled = 1; } if (labibb->notify_lab_vreg_ok_sts) if (labibb->notify_lab_vreg_ok_sts || labibb->detect_lab_sc) schedule_work(&labibb->lab_vreg_ok_work); return 0; Loading Loading @@ -2444,6 +2529,163 @@ static int qpnp_lab_regulator_is_enabled(struct regulator_dev *rdev) return labibb->lab_vreg.vreg_enabled; } static int qpnp_labibb_force_enable(struct qpnp_labibb *labibb) { int rc; if (labibb->skip_2nd_swire_cmd) { rc = qpnp_ibb_ps_config(labibb, false); if (rc < 0) { pr_err("Failed to disable IBB PS rc=%d\n", rc); return rc; } } if (!labibb->swire_control) { if (!labibb->standalone) return qpnp_labibb_regulator_enable(labibb); rc = qpnp_ibb_enable_standalone(labibb); if (rc < 0) { pr_err("enable ibb standalone failed, rc=%d\n", rc); return rc; } labibb->ibb_vreg.vreg_enabled = 1; rc = qpnp_lab_enable_standalone(labibb); if (rc < 0) { pr_err("enable lab standalone failed, rc=%d\n", rc); return rc; } labibb->lab_vreg.vreg_enabled = 1; } return 0; } #define SC_ERR_RECOVERY_DELAY_MS 250 #define SC_ERR_COUNT_INTERVAL_SEC 1 #define POLLING_SCP_DONE_COUNT 2 #define POLLING_SCP_DONE_INTERVAL_MS 5 static irqreturn_t labibb_sc_err_handler(int irq, void *_labibb) { int rc; u16 reg; u8 sc_err_mask, val; char *str; struct qpnp_labibb *labibb = (struct qpnp_labibb *)_labibb; bool in_sc_err, lab_en, ibb_en, scp_done = false; int count; if (irq == labibb->lab_vreg.lab_sc_irq) { reg = labibb->lab_base + REG_LAB_STATUS1; sc_err_mask = LAB_STATUS1_SC_DETECT_BIT; str = "LAB"; } else if (irq == labibb->ibb_vreg.ibb_sc_irq) { reg = labibb->ibb_base + REG_IBB_STATUS1; sc_err_mask = IBB_STATUS1_SC_DETECT_BIT; str = "IBB"; } else { return IRQ_HANDLED; } rc = qpnp_labibb_read(labibb, reg, &val, 1); if (rc < 0) { pr_err("Read 0x%x failed, rc=%d\n", reg, rc); return IRQ_HANDLED; } pr_debug("%s SC error triggered! %s_STATUS1 = %d\n", str, str, val); in_sc_err = !!(val & sc_err_mask); /* * The SC fault would trigger PBS to disable regulators * for protection. This would cause the SC_DETECT status being * cleared so that it's not able to get the SC fault status. * Check if LAB/IBB regulators are enabled in the driver but * disabled in hardware, this means a SC fault had happened * and SCP handling is completed by PBS. */ if (!in_sc_err) { count = POLLING_SCP_DONE_COUNT; do { reg = labibb->lab_base + REG_LAB_ENABLE_CTL; rc = qpnp_labibb_read(labibb, reg, &val, 1); if (rc < 0) { pr_err("Read 0x%x failed, rc=%d\n", reg, rc); return IRQ_HANDLED; } lab_en = !!(val & LAB_ENABLE_CTL_EN); reg = labibb->ibb_base + REG_IBB_ENABLE_CTL; rc = qpnp_labibb_read(labibb, reg, &val, 1); if (rc < 0) { pr_err("Read 0x%x failed, rc=%d\n", reg, rc); return IRQ_HANDLED; } ibb_en = !!(val & IBB_ENABLE_CTL_MODULE_EN); if (lab_en || ibb_en) msleep(POLLING_SCP_DONE_INTERVAL_MS); else break; } while ((lab_en || ibb_en) && count--); if (labibb->lab_vreg.vreg_enabled && labibb->ibb_vreg.vreg_enabled && !lab_en && !ibb_en) { pr_debug("LAB/IBB has been disabled by SCP\n"); scp_done = true; } } if (in_sc_err || scp_done) { if (hrtimer_active(&labibb->sc_err_check_timer) || hrtimer_callback_running(&labibb->sc_err_check_timer)) { labibb->sc_err_count++; } else { labibb->sc_err_count = 1; hrtimer_start(&labibb->sc_err_check_timer, ktime_set(SC_ERR_COUNT_INTERVAL_SEC, 0), HRTIMER_MODE_REL); } schedule_delayed_work(&labibb->sc_err_recovery_work, msecs_to_jiffies(SC_ERR_RECOVERY_DELAY_MS)); } return IRQ_HANDLED; } #define SC_FAULT_COUNT_MAX 4 static enum hrtimer_restart labibb_check_sc_err_count(struct hrtimer *timer) { struct qpnp_labibb *labibb = container_of(timer, struct qpnp_labibb, sc_err_check_timer); /* * if SC fault triggers more than 4 times in 1 second, * then disable the IRQs and leave as it. */ if (labibb->sc_err_count > SC_FAULT_COUNT_MAX) { disable_irq(labibb->lab_vreg.lab_sc_irq); disable_irq(labibb->ibb_vreg.ibb_sc_irq); } return HRTIMER_NORESTART; } static void labibb_sc_err_recovery_work(struct work_struct *work) { struct qpnp_labibb *labibb = container_of(work, struct qpnp_labibb, sc_err_recovery_work.work); int rc; labibb->ibb_vreg.vreg_enabled = 0; labibb->lab_vreg.vreg_enabled = 0; rc = qpnp_labibb_force_enable(labibb); if (rc < 0) pr_err("force enable labibb failed, rc=%d\n", rc); } static int qpnp_lab_regulator_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned int *selector) { Loading Loading @@ -2505,7 +2747,7 @@ static int qpnp_skip_swire_command(struct qpnp_labibb *labibb) pr_err("Failed to read ibb_status1 reg rc=%d\n", rc); return rc; } if ((reg & IBB_STATUS1_VREG_OK_MASK) == IBB_STATUS1_VREG_OK) if (reg & IBB_STATUS1_VREG_OK_BIT) break; /* poll delay */ Loading Loading @@ -2661,6 +2903,12 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb, labibb->notify_lab_vreg_ok_sts = of_property_read_bool(of_node, "qcom,notify-lab-vreg-ok-sts"); labibb->lab_vreg.sc_wait_time_ms = -EINVAL; if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE && labibb->detect_lab_sc) of_property_read_u32(of_node, "qcom,qpnp-lab-sc-wait-time-ms", &labibb->lab_vreg.sc_wait_time_ms); rc = of_property_read_u32(of_node, "qcom,qpnp-lab-soft-start", &(labibb->lab_vreg.soft_start)); if (!rc) { Loading Loading @@ -2833,6 +3081,18 @@ static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb, } } if (labibb->lab_vreg.lab_sc_irq != -EINVAL) { rc = devm_request_threaded_irq(labibb->dev, labibb->lab_vreg.lab_sc_irq, NULL, labibb_sc_err_handler, IRQF_ONESHOT | IRQF_TRIGGER_RISING, "lab-sc-err", labibb); if (rc) { pr_err("Failed to register 'lab-sc-err' irq rc=%d\n", rc); return rc; } } rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_MODULE_RDY, &val, 1); if (rc < 0) { Loading Loading @@ -3302,45 +3562,26 @@ static int qpnp_ibb_dt_init(struct qpnp_labibb *labibb, static int qpnp_ibb_regulator_enable(struct regulator_dev *rdev) { int rc, delay, retries = 10; u8 val; int rc = 0; struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); if (!labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) { if (labibb->sc_detected) { pr_info("Short circuit detected: disabled LAB/IBB rails\n"); return 0; } if (!labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) { if (!labibb->standalone) return qpnp_labibb_regulator_enable(labibb); rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN); rc = qpnp_ibb_enable_standalone(labibb); if (rc < 0) { pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc); pr_err("enable ibb standalone failed, rc=%d\n", rc); return rc; } delay = labibb->ibb_vreg.soft_start; while (retries--) { /* Wait for a small period before reading IBB_STATUS1 */ usleep_range(delay, delay + 100); rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_STATUS1, &val, 1); if (rc < 0) { pr_err("qpnp_ibb_regulator_enable read register %x failed rc = %d\n", REG_IBB_STATUS1, rc); return rc; } if (val & IBB_STATUS1_VREG_OK) break; } if (!(val & IBB_STATUS1_VREG_OK)) { pr_err("qpnp_ibb_regulator_enable failed\n"); return -EINVAL; } labibb->ibb_vreg.vreg_enabled = 1; } return 0; } Loading Loading @@ -3389,7 +3630,6 @@ static int qpnp_ibb_regulator_set_voltage(struct regulator_dev *rdev, return rc; } static int qpnp_ibb_regulator_get_voltage(struct regulator_dev *rdev) { struct qpnp_labibb *labibb = rdev_get_drvdata(rdev); Loading Loading @@ -3611,6 +3851,19 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb, labibb->ibb_vreg.pwrdn_dly = 0; } if (labibb->ibb_vreg.ibb_sc_irq != -EINVAL) { rc = devm_request_threaded_irq(labibb->dev, labibb->ibb_vreg.ibb_sc_irq, NULL, labibb_sc_err_handler, IRQF_ONESHOT | IRQF_TRIGGER_RISING, "ibb-sc-err", labibb); if (rc) { pr_err("Failed to register 'ibb-sc-err' irq rc=%d\n", rc); return rc; } } rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_MODULE_RDY, &val, 1); if (rc < 0) { Loading Loading @@ -3684,15 +3937,39 @@ static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb, static int qpnp_lab_register_irq(struct device_node *child, struct qpnp_labibb *labibb) { int rc = 0; if (is_lab_vreg_ok_irq_available(labibb)) { labibb->lab_vreg.lab_vreg_ok_irq = of_irq_get_byname(child, "lab-vreg-ok"); if (labibb->lab_vreg.lab_vreg_ok_irq < 0) { rc = of_irq_get_byname(child, "lab-vreg-ok"); if (rc < 0) { pr_err("Invalid lab-vreg-ok irq\n"); return -EINVAL; return rc; } labibb->lab_vreg.lab_vreg_ok_irq = rc; } labibb->lab_vreg.lab_sc_irq = -EINVAL; rc = of_irq_get_byname(child, "lab-sc-err"); if (rc < 0) pr_debug("Unable to get lab-sc-err, rc = %d\n", rc); else labibb->lab_vreg.lab_sc_irq = rc; return 0; } static int qpnp_ibb_register_irq(struct device_node *child, struct qpnp_labibb *labibb) { int rc; labibb->ibb_vreg.ibb_sc_irq = -EINVAL; rc = of_irq_get_byname(child, "ibb-sc-err"); if (rc < 0) pr_debug("Unable to get ibb-sc-err, rc = %d\n", rc); else labibb->ibb_vreg.ibb_sc_irq = rc; return 0; } Loading Loading @@ -3788,6 +4065,8 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev) if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) { labibb->mode = QPNP_LABIBB_AMOLED_MODE; /* Enable polling for LAB short circuit detection for PM660A */ labibb->detect_lab_sc = true; } else { rc = of_property_read_string(labibb->dev->of_node, "qcom,qpnp-labibb-mode", &mode_name); Loading Loading @@ -3896,6 +4175,7 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev) case QPNP_IBB_TYPE: labibb->ibb_base = base; labibb->ibb_dig_major = revision; qpnp_ibb_register_irq(child, labibb); rc = register_qpnp_ibb_regulator(labibb, child); if (rc < 0) goto fail_registration; Loading @@ -3919,6 +4199,11 @@ static int qpnp_labibb_regulator_probe(struct platform_device *pdev) } INIT_WORK(&labibb->lab_vreg_ok_work, qpnp_lab_vreg_notifier_work); INIT_DELAYED_WORK(&labibb->sc_err_recovery_work, labibb_sc_err_recovery_work); hrtimer_init(&labibb->sc_err_check_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); labibb->sc_err_check_timer.function = labibb_check_sc_err_count; dev_set_drvdata(&pdev->dev, labibb); pr_info("LAB/IBB registered successfully, lab_vreg enable=%d ibb_vreg enable=%d swire_control=%d\n", labibb->lab_vreg.vreg_enabled, Loading
drivers/regulator/qpnp-oledb-regulator.c +46 −2 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ #include <linux/regulator/of_regulator.h> #include <linux/regulator/qpnp-labibb-regulator.h> #include <linux/qpnp/qpnp-pbs.h> #include <linux/qpnp/qpnp-revid.h> #define QPNP_OLEDB_REGULATOR_DRIVER_NAME "qcom,qpnp-oledb-regulator" #define OLEDB_VOUT_STEP_MV 100 Loading Loading @@ -162,6 +163,7 @@ struct qpnp_oledb { struct notifier_block oledb_nb; struct mutex bus_lock; struct device_node *pbs_dev_node; struct pmic_revid_data *pmic_rev_id; u32 base; u8 mod_enable; Loading @@ -181,6 +183,8 @@ struct qpnp_oledb { bool dynamic_ext_pinctl_config; bool pbs_control; bool force_pd_control; bool handle_lab_sc_notification; bool lab_sc_detected; }; static const u16 oledb_warmup_dly_ns[] = {6700, 13300, 26700, 53400}; Loading Loading @@ -275,6 +279,11 @@ static int qpnp_oledb_regulator_enable(struct regulator_dev *rdev) struct qpnp_oledb *oledb = rdev_get_drvdata(rdev); if (oledb->lab_sc_detected == true) { pr_info("Short circuit detected: Disabled OLEDB rail\n"); return 0; } if (oledb->ext_pin_control) { rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_EXT_PIN_CTL, &val, 1); Loading Loading @@ -1085,8 +1094,22 @@ static int qpnp_oledb_parse_fast_precharge(struct qpnp_oledb *oledb) static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb) { int rc = 0; struct device_node *revid_dev_node; struct device_node *of_node = oledb->dev->of_node; revid_dev_node = of_parse_phandle(oledb->dev->of_node, "qcom,pmic-revid", 0); if (!revid_dev_node) { pr_err("Missing qcom,pmic-revid property - driver failed\n"); return -EINVAL; } oledb->pmic_rev_id = get_revid_data(revid_dev_node); if (IS_ERR(oledb->pmic_rev_id)) { pr_debug("Unable to get revid data\n"); return -EPROBE_DEFER; } oledb->swire_control = of_property_read_bool(of_node, "qcom,swire-control"); Loading Loading @@ -1227,15 +1250,32 @@ static int qpnp_labibb_notifier_cb(struct notifier_block *nb, unsigned long action, void *data) { int rc = 0; u8 val; struct qpnp_oledb *oledb = container_of(nb, struct qpnp_oledb, oledb_nb); if (action == LAB_VREG_NOT_OK) { /* short circuit detected. Disable OLEDB module */ val = 0; rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_MODULE_RDY, &val, 1); if (rc < 0) { pr_err("Failed to write MODULE_RDY rc=%d\n", rc); return NOTIFY_STOP; } oledb->lab_sc_detected = true; oledb->mod_enable = false; pr_crit("LAB SC detected, disabling OLEDB forever!\n"); } if (action == LAB_VREG_OK) { /* Disable SWIRE pull down control and enable via spmi mode */ rc = qpnp_oledb_force_pulldown_config(oledb); if (rc < 0) if (rc < 0) { pr_err("Failed to config force pull down\n"); return NOTIFY_STOP; } } return NOTIFY_OK; } Loading Loading @@ -1281,7 +1321,11 @@ static int qpnp_oledb_regulator_probe(struct platform_device *pdev) return rc; } if (oledb->force_pd_control) { /* Enable LAB short circuit notification support */ if (oledb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) oledb->handle_lab_sc_notification = true; if (oledb->force_pd_control || oledb->handle_lab_sc_notification) { oledb->oledb_nb.notifier_call = qpnp_labibb_notifier_cb; rc = qpnp_labibb_notifier_register(&oledb->oledb_nb); if (rc < 0) { Loading