Loading Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt +6 −0 Original line number Diff line number Diff line Loading @@ -241,6 +241,12 @@ Charger specific properties: connector THERM, only valid values are (0/30/100/400). If not specified 100K is used as default pull-up. - qcom,moisture-protection-enable Usage: optional Value type: bool Definition: Boolean flag which when present enables mositure protection feature. ============================================= Second Level Nodes - SMB5 Charger Peripherals ============================================= Loading drivers/power/supply/qcom/qpnp-smb5.c +40 −8 Original line number Diff line number Diff line Loading @@ -283,6 +283,8 @@ static int smb5_chg_config_init(struct smb5 *chip) case PMI632_SUBTYPE: chip->chg.smb_version = PMI632_SUBTYPE; chg->wa_flags |= WEAK_ADAPTER_WA; if (pmic_rev_id->rev4 >= 2) chg->wa_flags |= MOISTURE_PROTECTION_WA; chg->param = smb5_pmi632_params; chg->use_extcon = true; chg->name = "pmi632_charger"; Loading Loading @@ -499,6 +501,9 @@ static int smb5_parse_dt(struct smb5 *chip) of_property_read_u32(node, "qcom,connector-internal-pull-kohm", &chg->connector_pull_up); chg->moisture_protection_enabled = of_property_read_bool(node, "qcom,moisture-protection-enable"); return 0; } Loading Loading @@ -617,6 +622,7 @@ static enum power_supply_property smb5_usb_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_HVDCP_OPTI_ALLOWED, POWER_SUPPLY_PROP_QC_OPTI_DISABLE, POWER_SUPPLY_PROP_MOISTURE_DETECTED, }; static int smb5_usb_get_prop(struct power_supply *psy, Loading Loading @@ -755,6 +761,9 @@ static int smb5_usb_get_prop(struct power_supply *psy, if (chg->hw_connector_mitigation) val->intval |= POWER_SUPPLY_QC_CTM_DISABLE; break; case POWER_SUPPLY_PROP_MOISTURE_DETECTED: val->intval = chg->moisture_present; break; default: pr_err("get prop %d is not supported in usb\n", psp); rc = -EINVAL; Loading Loading @@ -1646,14 +1655,6 @@ static int smb5_configure_typec(struct smb_charger *chg) return rc; } rc = smblib_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG, TYPEC_WATER_DETECTION_INT_EN_BIT); if (rc < 0) { dev_err(chg->dev, "Couldn't configure Type-C interrupts rc=%d\n", rc); return rc; } rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG, EN_TRY_SNK_BIT, EN_TRY_SNK_BIT); if (rc < 0) { Loading Loading @@ -1692,6 +1693,37 @@ static int smb5_configure_micro_usb(struct smb_charger *chg) return rc; } if (chg->moisture_protection_enabled && (chg->wa_flags & MOISTURE_PROTECTION_WA)) { /* Enable moisture detection interrupt */ rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG, TYPEC_WATER_DETECTION_INT_EN_BIT, TYPEC_WATER_DETECTION_INT_EN_BIT); if (rc < 0) { dev_err(chg->dev, "Couldn't enable moisture detection interrupt rc=%d\n", rc); return rc; } /* Enable uUSB factory mode */ rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG, EN_MICRO_USB_FACTORY_MODE_BIT, EN_MICRO_USB_FACTORY_MODE_BIT); if (rc < 0) { dev_err(chg->dev, "Couldn't enable uUSB factory mode c=%d\n", rc); return rc; } /* Disable periodic monitoring of CC_ID pin */ rc = smblib_write(chg, TYPEC_U_USB_WATER_PROTECTION_CFG_REG, 0); if (rc < 0) { dev_err(chg->dev, "Couldn't disable periodic monitoring of CC_ID rc=%d\n", rc); return rc; } } return rc; } Loading drivers/power/supply/qcom/smb5-lib.c +226 −4 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <linux/irq.h> #include <linux/pmic-voter.h> #include <linux/of_batterydata.h> #include <linux/alarmtimer.h> #include "smb5-lib.h" #include "smb5-reg.h" #include "battery.h" Loading Loading @@ -1045,6 +1046,100 @@ int smblib_get_icl_current(struct smb_charger *chg, int *icl_ua) return 0; } /******************** * Moisture Protection * ********************/ #define MICRO_USB_DETECTION_ON_TIME_20_MS 0x08 #define MICRO_USB_DETECTION_PERIOD_X_100 0x03 #define U_USB_STATUS_WATER_PRESENT 0x00 static int smblib_set_moisture_protection(struct smb_charger *chg, bool enable) { int rc = 0; if (chg->moisture_present == enable) { smblib_dbg(chg, PR_MISC, "No change in moisture protection status\n"); return rc; } if (enable) { chg->moisture_present = true; /* Disable uUSB factory mode detection */ rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG, EN_MICRO_USB_FACTORY_MODE_BIT, 0); if (rc < 0) { smblib_err(chg, "Couldn't disable uUSB factory mode detection rc=%d\n", rc); return rc; } /* Disable moisture detection and uUSB state change interrupt */ rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG, TYPEC_WATER_DETECTION_INT_EN_BIT | MICRO_USB_STATE_CHANGE_INT_EN_BIT, 0); if (rc < 0) { smblib_err(chg, "Couldn't disable moisture detection interrupt rc=%d\n", rc); return rc; } /* Set 1% duty cycle on ID detection */ rc = smblib_masked_write(chg, TYPEC_U_USB_WATER_PROTECTION_CFG_REG, EN_MICRO_USB_WATER_PROTECTION_BIT | MICRO_USB_DETECTION_ON_TIME_CFG_MASK | MICRO_USB_DETECTION_PERIOD_CFG_MASK, EN_MICRO_USB_WATER_PROTECTION_BIT | MICRO_USB_DETECTION_ON_TIME_20_MS | MICRO_USB_DETECTION_PERIOD_X_100); if (rc < 0) { smblib_err(chg, "Couldn't set 1 percent CC_ID duty cycle rc=%d\n", rc); return rc; } vote(chg->usb_icl_votable, MOISTURE_VOTER, true, 0); } else { chg->moisture_present = false; vote(chg->usb_icl_votable, MOISTURE_VOTER, false, 0); /* Enable moisture detection and uUSB state change interrupt */ rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG, TYPEC_WATER_DETECTION_INT_EN_BIT | MICRO_USB_STATE_CHANGE_INT_EN_BIT, TYPEC_WATER_DETECTION_INT_EN_BIT | MICRO_USB_STATE_CHANGE_INT_EN_BIT); if (rc < 0) { smblib_err(chg, "Couldn't enable moisture detection and uUSB state change interrupt rc=%d\n", rc); return rc; } /* Disable periodic monitoring of CC_ID pin */ rc = smblib_write(chg, TYPEC_U_USB_WATER_PROTECTION_CFG_REG, 0); if (rc < 0) { smblib_err(chg, "Couldn't disable 1 percent CC_ID duty cycle rc=%d\n", rc); return rc; } /* Enable uUSB factory mode detection */ rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG, EN_MICRO_USB_FACTORY_MODE_BIT, EN_MICRO_USB_FACTORY_MODE_BIT); if (rc < 0) { smblib_err(chg, "Couldn't disable uUSB factory mode detection rc=%d\n", rc); return rc; } } smblib_dbg(chg, PR_MISC, "Moisture protection %s\n", chg->moisture_present ? "enabled" : "disabled"); return rc; } /********************* * VOTABLE CALLBACKS * *********************/ Loading Loading @@ -3495,11 +3590,28 @@ irqreturn_t typec_or_rid_detection_change_irq_handler(int irq, void *data) struct smb_charger *chg = irq_data->parent_data; if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) { if (chg->moisture_protection_enabled && (chg->wa_flags & MOISTURE_PROTECTION_WA)) { /* * Adding pm_stay_awake as because pm_relax is called * on exit path from the work routine. */ pm_stay_awake(chg->dev); schedule_work(&chg->moisture_protection_work); } cancel_delayed_work_sync(&chg->uusb_otg_work); /* * Skip OTG enablement if RID interrupt triggers with moisture * protection still enabled. */ if (!chg->moisture_present) { vote(chg->awake_votable, OTG_DELAY_VOTER, true, 0); smblib_dbg(chg, PR_INTERRUPT, "Scheduling OTG work\n"); schedule_delayed_work(&chg->uusb_otg_work, msecs_to_jiffies(chg->otg_delay_ms)); } return IRQ_HANDLED; } Loading Loading @@ -3867,6 +3979,96 @@ static void smblib_pl_enable_work(struct work_struct *work) vote(chg->awake_votable, PL_DELAY_VOTER, false, 0); } #define MOISTURE_PROTECTION_CHECK_DELAY_MS 300000 /* 5 mins */ static void smblib_moisture_protection_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, moisture_protection_work); int rc; bool usb_plugged_in; u8 stat; /* * Disable 1% duty cycle on CC_ID pin and enable uUSB factory mode * detection to track any change on RID, as interrupts are disable. */ rc = smblib_write(chg, TYPEC_U_USB_WATER_PROTECTION_CFG_REG, 0); if (rc < 0) { smblib_err(chg, "Couldn't disable periodic monitoring of CC_ID rc=%d\n", rc); goto out; } rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG, EN_MICRO_USB_FACTORY_MODE_BIT, EN_MICRO_USB_FACTORY_MODE_BIT); if (rc < 0) { smblib_err(chg, "Couldn't enable uUSB factory mode detection rc=%d\n", rc); goto out; } /* * Add a delay of 100ms to allow change in rid to reflect on * status registers. */ msleep(100); rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc); goto out; } usb_plugged_in = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); /* Check uUSB status for moisture presence */ rc = smblib_read(chg, TYPEC_U_USB_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read TYPE_C_U_USB_STATUS_REG rc=%d\n", rc); goto out; } /* * Factory mode detection happens in case of USB plugged-in by using * a different current source of 2uA which can hamper moisture * detection. Since factory mode is not supported in kernel, factory * mode detection can be considered as equivalent to presence of * moisture. */ if (stat == U_USB_STATUS_WATER_PRESENT || stat == U_USB_FMB1_BIT || stat == U_USB_FMB2_BIT || (usb_plugged_in && stat == U_USB_FLOAT1_BIT)) { smblib_set_moisture_protection(chg, true); alarm_start_relative(&chg->moisture_protection_alarm, ms_to_ktime(MOISTURE_PROTECTION_CHECK_DELAY_MS)); } else { smblib_set_moisture_protection(chg, false); rc = alarm_cancel(&chg->moisture_protection_alarm); if (rc < 0) smblib_err(chg, "Couldn't cancel moisture protection alarm\n"); } out: pm_relax(chg->dev); } static enum alarmtimer_restart moisture_protection_alarm_cb(struct alarm *alarm, ktime_t now) { struct smb_charger *chg = container_of(alarm, struct smb_charger, moisture_protection_alarm); smblib_dbg(chg, PR_MISC, "moisture Protection Alarm Triggered %lld\n", ktime_to_ms(now)); /* Atomic context, cannot use voter */ pm_stay_awake(chg->dev); schedule_work(&chg->moisture_protection_work); return ALARMTIMER_NORESTART; } #define JEITA_SOFT 0 #define JEITA_HARD 1 static int smblib_update_jeita(struct smb_charger *chg, u32 *thresholds, Loading Loading @@ -4069,6 +4271,21 @@ int smblib_init(struct smb_charger *chg) INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work); INIT_DELAYED_WORK(&chg->uusb_otg_work, smblib_uusb_otg_work); INIT_DELAYED_WORK(&chg->bb_removal_work, smblib_bb_removal_work); if (chg->moisture_protection_enabled && (chg->wa_flags & MOISTURE_PROTECTION_WA)) { INIT_WORK(&chg->moisture_protection_work, smblib_moisture_protection_work); if (alarmtimer_get_rtcdev()) { alarm_init(&chg->moisture_protection_alarm, ALARM_BOOTTIME, moisture_protection_alarm_cb); } else { smblib_err(chg, "Failed to initialize moisture protection alarm\n"); return -ENODEV; } } chg->fake_capacity = -EINVAL; chg->fake_input_current_limited = -EINVAL; chg->fake_batt_status = -EINVAL; Loading Loading @@ -4130,6 +4347,11 @@ int smblib_deinit(struct smb_charger *chg) { switch (chg->mode) { case PARALLEL_MASTER: if (chg->moisture_protection_enabled && (chg->wa_flags & MOISTURE_PROTECTION_WA)) { alarm_cancel(&chg->moisture_protection_alarm); cancel_work_sync(&chg->moisture_protection_work); } cancel_work_sync(&chg->bms_update_work); cancel_work_sync(&chg->jeita_update_work); cancel_work_sync(&chg->pl_update_work); Loading drivers/power/supply/qcom/smb5-lib.h +9 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <linux/regulator/driver.h> #include <linux/regulator/consumer.h> #include <linux/extcon.h> #include <linux/alarmtimer.h> #include "storm-watch.h" enum print_reason { Loading Loading @@ -68,6 +69,7 @@ enum print_reason { #define HW_LIMIT_VOTER "HW_LIMIT_VOTER" #define FORCE_RECHARGE_VOTER "FORCE_RECHARGE_VOTER" #define AICL_THRESHOLD_VOTER "AICL_THRESHOLD_VOTER" #define MOISTURE_VOTER "MOISTURE_VOTER" #define BOOST_BACK_STORM_COUNT 3 #define WEAK_CHG_STORM_COUNT 8 Loading Loading @@ -99,6 +101,7 @@ enum sink_src_mode { enum { BOOST_BACK_WA = BIT(0), WEAK_ADAPTER_WA = BIT(1), MOISTURE_PROTECTION_WA = BIT(2), }; enum { Loading Loading @@ -339,6 +342,7 @@ struct smb_charger { struct work_struct bms_update_work; struct work_struct pl_update_work; struct work_struct jeita_update_work; struct work_struct moisture_protection_work; struct delayed_work ps_change_timeout_work; struct delayed_work clear_hdc_work; struct delayed_work icl_change_work; Loading @@ -346,6 +350,9 @@ struct smb_charger { struct delayed_work uusb_otg_work; struct delayed_work bb_removal_work; /* alarm */ struct alarm moisture_protection_alarm; /* pd */ int voltage_min_uv; int voltage_max_uv; Loading Loading @@ -394,6 +401,8 @@ struct smb_charger { int aicl_cont_threshold_mv; int default_aicl_cont_threshold_mv; bool aicl_max_reached; bool moisture_present; bool moisture_protection_enabled; /* workaround flag */ u32 wa_flags; Loading drivers/power/supply/qcom/smb5-reg.h +12 −0 Original line number Diff line number Diff line Loading @@ -126,6 +126,7 @@ enum { #define USBIN_SUSPEND_STS_BIT BIT(6) #define USE_USBIN_BIT BIT(4) #define USE_DCIN_BIT BIT(3) #define POWER_PATH_MASK GENMASK(2, 1) #define VALID_INPUT_POWER_SOURCE_STS_BIT BIT(0) #define DCDC_CMD_OTG_REG (DCDC_BASE + 0x40) Loading Loading @@ -294,6 +295,7 @@ enum { #define TYPEC_ATTACH_DETACH_STATE_BIT BIT(5) #define TYPE_C_MISC_STATUS_REG (TYPEC_BASE + 0x0B) #define TYPEC_WATER_DETECTION_STATUS_BIT BIT(7) #define SNK_SRC_MODE_BIT BIT(6) #define TYPEC_VBUS_ERROR_STATUS_BIT BIT(4) #define CC_ORIENTATION_BIT BIT(1) Loading @@ -306,6 +308,10 @@ enum { #define TYPEC_U_USB_STATUS_REG (TYPEC_BASE + 0x0F) #define U_USB_GROUND_NOVBUS_BIT BIT(6) #define U_USB_GROUND_BIT BIT(4) #define U_USB_FMB1_BIT BIT(3) #define U_USB_FLOAT1_BIT BIT(2) #define U_USB_FMB2_BIT BIT(1) #define U_USB_FLOAT2_BIT BIT(0) #define TYPE_C_MODE_CFG_REG (TYPEC_BASE + 0x44) #define TYPEC_POWER_ROLE_CMD_MASK GENMASK(2, 1) Loading Loading @@ -351,8 +357,14 @@ enum { #define REDUCE_TCCDEBOUNCE_TO_2MS_BIT BIT(2) #define TYPEC_U_USB_CFG_REG (TYPEC_BASE + 0x70) #define EN_MICRO_USB_FACTORY_MODE_BIT BIT(1) #define EN_MICRO_USB_MODE_BIT BIT(0) #define TYPEC_U_USB_WATER_PROTECTION_CFG_REG (TYPEC_BASE + 0x72) #define EN_MICRO_USB_WATER_PROTECTION_BIT BIT(4) #define MICRO_USB_DETECTION_ON_TIME_CFG_MASK GENMASK(3, 2) #define MICRO_USB_DETECTION_PERIOD_CFG_MASK GENMASK(1, 0) #define TYPEC_MICRO_USB_MODE_REG (TYPEC_BASE + 0x73) #define MICRO_USB_MODE_ONLY_BIT BIT(0) /******************************** Loading Loading
Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt +6 −0 Original line number Diff line number Diff line Loading @@ -241,6 +241,12 @@ Charger specific properties: connector THERM, only valid values are (0/30/100/400). If not specified 100K is used as default pull-up. - qcom,moisture-protection-enable Usage: optional Value type: bool Definition: Boolean flag which when present enables mositure protection feature. ============================================= Second Level Nodes - SMB5 Charger Peripherals ============================================= Loading
drivers/power/supply/qcom/qpnp-smb5.c +40 −8 Original line number Diff line number Diff line Loading @@ -283,6 +283,8 @@ static int smb5_chg_config_init(struct smb5 *chip) case PMI632_SUBTYPE: chip->chg.smb_version = PMI632_SUBTYPE; chg->wa_flags |= WEAK_ADAPTER_WA; if (pmic_rev_id->rev4 >= 2) chg->wa_flags |= MOISTURE_PROTECTION_WA; chg->param = smb5_pmi632_params; chg->use_extcon = true; chg->name = "pmi632_charger"; Loading Loading @@ -499,6 +501,9 @@ static int smb5_parse_dt(struct smb5 *chip) of_property_read_u32(node, "qcom,connector-internal-pull-kohm", &chg->connector_pull_up); chg->moisture_protection_enabled = of_property_read_bool(node, "qcom,moisture-protection-enable"); return 0; } Loading Loading @@ -617,6 +622,7 @@ static enum power_supply_property smb5_usb_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_HVDCP_OPTI_ALLOWED, POWER_SUPPLY_PROP_QC_OPTI_DISABLE, POWER_SUPPLY_PROP_MOISTURE_DETECTED, }; static int smb5_usb_get_prop(struct power_supply *psy, Loading Loading @@ -755,6 +761,9 @@ static int smb5_usb_get_prop(struct power_supply *psy, if (chg->hw_connector_mitigation) val->intval |= POWER_SUPPLY_QC_CTM_DISABLE; break; case POWER_SUPPLY_PROP_MOISTURE_DETECTED: val->intval = chg->moisture_present; break; default: pr_err("get prop %d is not supported in usb\n", psp); rc = -EINVAL; Loading Loading @@ -1646,14 +1655,6 @@ static int smb5_configure_typec(struct smb_charger *chg) return rc; } rc = smblib_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG, TYPEC_WATER_DETECTION_INT_EN_BIT); if (rc < 0) { dev_err(chg->dev, "Couldn't configure Type-C interrupts rc=%d\n", rc); return rc; } rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG, EN_TRY_SNK_BIT, EN_TRY_SNK_BIT); if (rc < 0) { Loading Loading @@ -1692,6 +1693,37 @@ static int smb5_configure_micro_usb(struct smb_charger *chg) return rc; } if (chg->moisture_protection_enabled && (chg->wa_flags & MOISTURE_PROTECTION_WA)) { /* Enable moisture detection interrupt */ rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG, TYPEC_WATER_DETECTION_INT_EN_BIT, TYPEC_WATER_DETECTION_INT_EN_BIT); if (rc < 0) { dev_err(chg->dev, "Couldn't enable moisture detection interrupt rc=%d\n", rc); return rc; } /* Enable uUSB factory mode */ rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG, EN_MICRO_USB_FACTORY_MODE_BIT, EN_MICRO_USB_FACTORY_MODE_BIT); if (rc < 0) { dev_err(chg->dev, "Couldn't enable uUSB factory mode c=%d\n", rc); return rc; } /* Disable periodic monitoring of CC_ID pin */ rc = smblib_write(chg, TYPEC_U_USB_WATER_PROTECTION_CFG_REG, 0); if (rc < 0) { dev_err(chg->dev, "Couldn't disable periodic monitoring of CC_ID rc=%d\n", rc); return rc; } } return rc; } Loading
drivers/power/supply/qcom/smb5-lib.c +226 −4 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include <linux/irq.h> #include <linux/pmic-voter.h> #include <linux/of_batterydata.h> #include <linux/alarmtimer.h> #include "smb5-lib.h" #include "smb5-reg.h" #include "battery.h" Loading Loading @@ -1045,6 +1046,100 @@ int smblib_get_icl_current(struct smb_charger *chg, int *icl_ua) return 0; } /******************** * Moisture Protection * ********************/ #define MICRO_USB_DETECTION_ON_TIME_20_MS 0x08 #define MICRO_USB_DETECTION_PERIOD_X_100 0x03 #define U_USB_STATUS_WATER_PRESENT 0x00 static int smblib_set_moisture_protection(struct smb_charger *chg, bool enable) { int rc = 0; if (chg->moisture_present == enable) { smblib_dbg(chg, PR_MISC, "No change in moisture protection status\n"); return rc; } if (enable) { chg->moisture_present = true; /* Disable uUSB factory mode detection */ rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG, EN_MICRO_USB_FACTORY_MODE_BIT, 0); if (rc < 0) { smblib_err(chg, "Couldn't disable uUSB factory mode detection rc=%d\n", rc); return rc; } /* Disable moisture detection and uUSB state change interrupt */ rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG, TYPEC_WATER_DETECTION_INT_EN_BIT | MICRO_USB_STATE_CHANGE_INT_EN_BIT, 0); if (rc < 0) { smblib_err(chg, "Couldn't disable moisture detection interrupt rc=%d\n", rc); return rc; } /* Set 1% duty cycle on ID detection */ rc = smblib_masked_write(chg, TYPEC_U_USB_WATER_PROTECTION_CFG_REG, EN_MICRO_USB_WATER_PROTECTION_BIT | MICRO_USB_DETECTION_ON_TIME_CFG_MASK | MICRO_USB_DETECTION_PERIOD_CFG_MASK, EN_MICRO_USB_WATER_PROTECTION_BIT | MICRO_USB_DETECTION_ON_TIME_20_MS | MICRO_USB_DETECTION_PERIOD_X_100); if (rc < 0) { smblib_err(chg, "Couldn't set 1 percent CC_ID duty cycle rc=%d\n", rc); return rc; } vote(chg->usb_icl_votable, MOISTURE_VOTER, true, 0); } else { chg->moisture_present = false; vote(chg->usb_icl_votable, MOISTURE_VOTER, false, 0); /* Enable moisture detection and uUSB state change interrupt */ rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG, TYPEC_WATER_DETECTION_INT_EN_BIT | MICRO_USB_STATE_CHANGE_INT_EN_BIT, TYPEC_WATER_DETECTION_INT_EN_BIT | MICRO_USB_STATE_CHANGE_INT_EN_BIT); if (rc < 0) { smblib_err(chg, "Couldn't enable moisture detection and uUSB state change interrupt rc=%d\n", rc); return rc; } /* Disable periodic monitoring of CC_ID pin */ rc = smblib_write(chg, TYPEC_U_USB_WATER_PROTECTION_CFG_REG, 0); if (rc < 0) { smblib_err(chg, "Couldn't disable 1 percent CC_ID duty cycle rc=%d\n", rc); return rc; } /* Enable uUSB factory mode detection */ rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG, EN_MICRO_USB_FACTORY_MODE_BIT, EN_MICRO_USB_FACTORY_MODE_BIT); if (rc < 0) { smblib_err(chg, "Couldn't disable uUSB factory mode detection rc=%d\n", rc); return rc; } } smblib_dbg(chg, PR_MISC, "Moisture protection %s\n", chg->moisture_present ? "enabled" : "disabled"); return rc; } /********************* * VOTABLE CALLBACKS * *********************/ Loading Loading @@ -3495,11 +3590,28 @@ irqreturn_t typec_or_rid_detection_change_irq_handler(int irq, void *data) struct smb_charger *chg = irq_data->parent_data; if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) { if (chg->moisture_protection_enabled && (chg->wa_flags & MOISTURE_PROTECTION_WA)) { /* * Adding pm_stay_awake as because pm_relax is called * on exit path from the work routine. */ pm_stay_awake(chg->dev); schedule_work(&chg->moisture_protection_work); } cancel_delayed_work_sync(&chg->uusb_otg_work); /* * Skip OTG enablement if RID interrupt triggers with moisture * protection still enabled. */ if (!chg->moisture_present) { vote(chg->awake_votable, OTG_DELAY_VOTER, true, 0); smblib_dbg(chg, PR_INTERRUPT, "Scheduling OTG work\n"); schedule_delayed_work(&chg->uusb_otg_work, msecs_to_jiffies(chg->otg_delay_ms)); } return IRQ_HANDLED; } Loading Loading @@ -3867,6 +3979,96 @@ static void smblib_pl_enable_work(struct work_struct *work) vote(chg->awake_votable, PL_DELAY_VOTER, false, 0); } #define MOISTURE_PROTECTION_CHECK_DELAY_MS 300000 /* 5 mins */ static void smblib_moisture_protection_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, moisture_protection_work); int rc; bool usb_plugged_in; u8 stat; /* * Disable 1% duty cycle on CC_ID pin and enable uUSB factory mode * detection to track any change on RID, as interrupts are disable. */ rc = smblib_write(chg, TYPEC_U_USB_WATER_PROTECTION_CFG_REG, 0); if (rc < 0) { smblib_err(chg, "Couldn't disable periodic monitoring of CC_ID rc=%d\n", rc); goto out; } rc = smblib_masked_write(chg, TYPEC_U_USB_CFG_REG, EN_MICRO_USB_FACTORY_MODE_BIT, EN_MICRO_USB_FACTORY_MODE_BIT); if (rc < 0) { smblib_err(chg, "Couldn't enable uUSB factory mode detection rc=%d\n", rc); goto out; } /* * Add a delay of 100ms to allow change in rid to reflect on * status registers. */ msleep(100); rc = smblib_read(chg, USBIN_BASE + INT_RT_STS_OFFSET, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read USB_INT_RT_STS rc=%d\n", rc); goto out; } usb_plugged_in = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); /* Check uUSB status for moisture presence */ rc = smblib_read(chg, TYPEC_U_USB_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read TYPE_C_U_USB_STATUS_REG rc=%d\n", rc); goto out; } /* * Factory mode detection happens in case of USB plugged-in by using * a different current source of 2uA which can hamper moisture * detection. Since factory mode is not supported in kernel, factory * mode detection can be considered as equivalent to presence of * moisture. */ if (stat == U_USB_STATUS_WATER_PRESENT || stat == U_USB_FMB1_BIT || stat == U_USB_FMB2_BIT || (usb_plugged_in && stat == U_USB_FLOAT1_BIT)) { smblib_set_moisture_protection(chg, true); alarm_start_relative(&chg->moisture_protection_alarm, ms_to_ktime(MOISTURE_PROTECTION_CHECK_DELAY_MS)); } else { smblib_set_moisture_protection(chg, false); rc = alarm_cancel(&chg->moisture_protection_alarm); if (rc < 0) smblib_err(chg, "Couldn't cancel moisture protection alarm\n"); } out: pm_relax(chg->dev); } static enum alarmtimer_restart moisture_protection_alarm_cb(struct alarm *alarm, ktime_t now) { struct smb_charger *chg = container_of(alarm, struct smb_charger, moisture_protection_alarm); smblib_dbg(chg, PR_MISC, "moisture Protection Alarm Triggered %lld\n", ktime_to_ms(now)); /* Atomic context, cannot use voter */ pm_stay_awake(chg->dev); schedule_work(&chg->moisture_protection_work); return ALARMTIMER_NORESTART; } #define JEITA_SOFT 0 #define JEITA_HARD 1 static int smblib_update_jeita(struct smb_charger *chg, u32 *thresholds, Loading Loading @@ -4069,6 +4271,21 @@ int smblib_init(struct smb_charger *chg) INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work); INIT_DELAYED_WORK(&chg->uusb_otg_work, smblib_uusb_otg_work); INIT_DELAYED_WORK(&chg->bb_removal_work, smblib_bb_removal_work); if (chg->moisture_protection_enabled && (chg->wa_flags & MOISTURE_PROTECTION_WA)) { INIT_WORK(&chg->moisture_protection_work, smblib_moisture_protection_work); if (alarmtimer_get_rtcdev()) { alarm_init(&chg->moisture_protection_alarm, ALARM_BOOTTIME, moisture_protection_alarm_cb); } else { smblib_err(chg, "Failed to initialize moisture protection alarm\n"); return -ENODEV; } } chg->fake_capacity = -EINVAL; chg->fake_input_current_limited = -EINVAL; chg->fake_batt_status = -EINVAL; Loading Loading @@ -4130,6 +4347,11 @@ int smblib_deinit(struct smb_charger *chg) { switch (chg->mode) { case PARALLEL_MASTER: if (chg->moisture_protection_enabled && (chg->wa_flags & MOISTURE_PROTECTION_WA)) { alarm_cancel(&chg->moisture_protection_alarm); cancel_work_sync(&chg->moisture_protection_work); } cancel_work_sync(&chg->bms_update_work); cancel_work_sync(&chg->jeita_update_work); cancel_work_sync(&chg->pl_update_work); Loading
drivers/power/supply/qcom/smb5-lib.h +9 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <linux/regulator/driver.h> #include <linux/regulator/consumer.h> #include <linux/extcon.h> #include <linux/alarmtimer.h> #include "storm-watch.h" enum print_reason { Loading Loading @@ -68,6 +69,7 @@ enum print_reason { #define HW_LIMIT_VOTER "HW_LIMIT_VOTER" #define FORCE_RECHARGE_VOTER "FORCE_RECHARGE_VOTER" #define AICL_THRESHOLD_VOTER "AICL_THRESHOLD_VOTER" #define MOISTURE_VOTER "MOISTURE_VOTER" #define BOOST_BACK_STORM_COUNT 3 #define WEAK_CHG_STORM_COUNT 8 Loading Loading @@ -99,6 +101,7 @@ enum sink_src_mode { enum { BOOST_BACK_WA = BIT(0), WEAK_ADAPTER_WA = BIT(1), MOISTURE_PROTECTION_WA = BIT(2), }; enum { Loading Loading @@ -339,6 +342,7 @@ struct smb_charger { struct work_struct bms_update_work; struct work_struct pl_update_work; struct work_struct jeita_update_work; struct work_struct moisture_protection_work; struct delayed_work ps_change_timeout_work; struct delayed_work clear_hdc_work; struct delayed_work icl_change_work; Loading @@ -346,6 +350,9 @@ struct smb_charger { struct delayed_work uusb_otg_work; struct delayed_work bb_removal_work; /* alarm */ struct alarm moisture_protection_alarm; /* pd */ int voltage_min_uv; int voltage_max_uv; Loading Loading @@ -394,6 +401,8 @@ struct smb_charger { int aicl_cont_threshold_mv; int default_aicl_cont_threshold_mv; bool aicl_max_reached; bool moisture_present; bool moisture_protection_enabled; /* workaround flag */ u32 wa_flags; Loading
drivers/power/supply/qcom/smb5-reg.h +12 −0 Original line number Diff line number Diff line Loading @@ -126,6 +126,7 @@ enum { #define USBIN_SUSPEND_STS_BIT BIT(6) #define USE_USBIN_BIT BIT(4) #define USE_DCIN_BIT BIT(3) #define POWER_PATH_MASK GENMASK(2, 1) #define VALID_INPUT_POWER_SOURCE_STS_BIT BIT(0) #define DCDC_CMD_OTG_REG (DCDC_BASE + 0x40) Loading Loading @@ -294,6 +295,7 @@ enum { #define TYPEC_ATTACH_DETACH_STATE_BIT BIT(5) #define TYPE_C_MISC_STATUS_REG (TYPEC_BASE + 0x0B) #define TYPEC_WATER_DETECTION_STATUS_BIT BIT(7) #define SNK_SRC_MODE_BIT BIT(6) #define TYPEC_VBUS_ERROR_STATUS_BIT BIT(4) #define CC_ORIENTATION_BIT BIT(1) Loading @@ -306,6 +308,10 @@ enum { #define TYPEC_U_USB_STATUS_REG (TYPEC_BASE + 0x0F) #define U_USB_GROUND_NOVBUS_BIT BIT(6) #define U_USB_GROUND_BIT BIT(4) #define U_USB_FMB1_BIT BIT(3) #define U_USB_FLOAT1_BIT BIT(2) #define U_USB_FMB2_BIT BIT(1) #define U_USB_FLOAT2_BIT BIT(0) #define TYPE_C_MODE_CFG_REG (TYPEC_BASE + 0x44) #define TYPEC_POWER_ROLE_CMD_MASK GENMASK(2, 1) Loading Loading @@ -351,8 +357,14 @@ enum { #define REDUCE_TCCDEBOUNCE_TO_2MS_BIT BIT(2) #define TYPEC_U_USB_CFG_REG (TYPEC_BASE + 0x70) #define EN_MICRO_USB_FACTORY_MODE_BIT BIT(1) #define EN_MICRO_USB_MODE_BIT BIT(0) #define TYPEC_U_USB_WATER_PROTECTION_CFG_REG (TYPEC_BASE + 0x72) #define EN_MICRO_USB_WATER_PROTECTION_BIT BIT(4) #define MICRO_USB_DETECTION_ON_TIME_CFG_MASK GENMASK(3, 2) #define MICRO_USB_DETECTION_PERIOD_CFG_MASK GENMASK(1, 0) #define TYPEC_MICRO_USB_MODE_REG (TYPEC_BASE + 0x73) #define MICRO_USB_MODE_ONLY_BIT BIT(0) /******************************** Loading