diff --git a/drivers/power/supply/qcom/smb1398-charger.c b/drivers/power/supply/qcom/smb1398-charger.c index 25c0d95d2507..8133655a061b 100644 --- a/drivers/power/supply/qcom/smb1398-charger.c +++ b/drivers/power/supply/qcom/smb1398-charger.c @@ -180,6 +180,9 @@ #define PERPH0_CFG_SDCDC_REG 0x267A #define EN_WIN_UV_BIT BIT(7) +#define PERPH0_SOVP_CFG0_REG 0x2680 +#define CFG_OVP_IGNORE_UVLO BIT(5) + #define PERPH0_SSUPPLY_CFG0_REG 0x2682 #define EN_HV_OV_OPTION2_BIT BIT(7) #define EN_MV_OV_OPTION2_BIT BIT(5) @@ -308,6 +311,7 @@ struct smb1398_chip { struct power_supply *pre_regulator_psy; struct power_supply *batt_psy; struct power_supply *dc_psy; + struct power_supply *usb_psy; struct notifier_block nb; struct votable *awake_votable; @@ -355,6 +359,7 @@ struct smb1398_chip { bool slave_en; bool in_suspend; bool disabled; + bool usb_present; }; struct cp_iio_prop_channels { @@ -1044,6 +1049,23 @@ static char *div2_cp_get_model_name(struct smb1398_chip *chip) return "SMB1398_V1"; } +static int smb1398_toggle_uvlo(struct smb1398_chip *chip) +{ + int rc; + + rc = smb1398_masked_write(chip, PERPH0_SOVP_CFG0_REG, + CFG_OVP_IGNORE_UVLO, CFG_OVP_IGNORE_UVLO); + if (rc < 0) + dev_err(chip->dev, "Couldn't write IGNORE_UVLO rc=%d\n", rc); + + rc = smb1398_masked_write(chip, PERPH0_SOVP_CFG0_REG, + CFG_OVP_IGNORE_UVLO, 0); + if (rc < 0) + dev_err(chip->dev, "Couldn't write IGNORE_UVLO, rc=%d\n", rc); + + return rc; +} + static enum power_supply_property div2_cp_master_props[] = { POWER_SUPPLY_PROP_MODEL_NAME, }; @@ -1105,6 +1127,14 @@ static bool is_psy_voter_available(struct smb1398_chip *chip) } } + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) { + dev_dbg(chip->dev, "Couldn't find usb psy\n"); + return false; + } + } + if (!chip->dc_psy) { chip->dc_psy = power_supply_get_by_name("dc"); if (!chip->dc_psy) { @@ -1590,6 +1620,20 @@ static void smb1398_status_change_work(struct work_struct *work) if (!is_cutoff_soc_reached(chip)) vote(chip->div2_cp_disable_votable, CUTOFF_SOC_VOTER, false, 0); + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &pval); + if (rc < 0) { + dev_err(chip->dev, + "Couldn't get USB PRESENT status, rc=%d\n", rc); + goto out; + } + + if (chip->usb_present != !!pval.intval) { + chip->usb_present = !!pval.intval; + if (!chip->usb_present) /* USB has been removed */ + smb1398_toggle_uvlo(chip); + } + rc = cp_read_iio_prop(chip, QPNP_SMB5, SMB_EN_MODE, &val); if (rc < 0) { dev_err(chip->dev, "Couldn't get SMB_EN_MODE, rc=%d\n", rc); @@ -2968,6 +3012,10 @@ static void smb1398_shutdown(struct platform_device *pdev) rc = smb1398_div2_cp_switcher_en(chip, 0); if (rc < 0) dev_err(chip->dev, "Couldn't disable chip rc= %d\n", rc); + + rc = smb1398_toggle_uvlo(chip); + if (rc < 0) + dev_err(chip->dev, "Couldn't toggle uvlo rc= %d\n", rc); } static const struct dev_pm_ops smb1398_pm_ops = {