diff --git a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt index 21404dfc4b7b..12ac75a8608c 100644 --- a/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt +++ b/Documentation/devicetree/bindings/power/qcom-charger/qpnp-smb2.txt @@ -53,6 +53,12 @@ Charger specific properties: Definition: Specifies the USB input current limit in micro-amps. If the value is not present, 1.5Amps is used as default. +- qcom,usb-ocl-ua + Usage: optional + Value type: + Definition: Specifies the OTG output current limit in micro-amps. + If the value is not present, 1.5Amps is used as default + - qcom,dc-icl-ua Usage: optional Value type: diff --git a/drivers/power/qcom-charger/qpnp-smb2.c b/drivers/power/qcom-charger/qpnp-smb2.c index 1b63f51088ee..ee576d300054 100644 --- a/drivers/power/qcom-charger/qpnp-smb2.c +++ b/drivers/power/qcom-charger/qpnp-smb2.c @@ -58,6 +58,13 @@ static struct smb_params v1_params = { .max_u = 4800000, .step_u = 25000, }, + .otg_cl = { + .name = "usb otg current limit", + .reg = OTG_CURRENT_LIMIT_CFG_REG, + .min_u = 250000, + .max_u = 2000000, + .step_u = 250000, + }, .dc_icl = { .name = "dc input current limit", .reg = DCIN_CURRENT_LIMIT_CFG_REG, @@ -202,6 +209,7 @@ struct smb_dt_props { bool no_battery; int fcc_ua; int usb_icl_ua; + int otg_cl_ua; int dc_icl_ua; int fv_uv; int wipower_max_uw; @@ -226,6 +234,7 @@ module_param_named( pl_master_percent, __pl_master_percent, int, S_IRUSR | S_IWUSR ); +#define MICRO_1P5A 1500000 static int smb2_parse_dt(struct smb2 *chip) { struct smb_charger *chg = &chip->chg; @@ -277,6 +286,11 @@ static int smb2_parse_dt(struct smb2 *chip) if (rc < 0) chip->dt.usb_icl_ua = -EINVAL; + rc = of_property_read_u32(node, + "qcom,otg-cl-ua", &chip->dt.otg_cl_ua); + if (rc < 0) + chip->dt.otg_cl_ua = MICRO_1P5A; + rc = of_property_read_u32(node, "qcom,dc-icl-ua", &chip->dt.dc_icl_ua); if (rc < 0) @@ -981,6 +995,8 @@ static int smb2_init_hw(struct smb2 *chip) smblib_get_charge_param(chg, &chg->param.dc_icl, &chip->dt.dc_icl_ua); + chg->otg_cl_ua = chip->dt.otg_cl_ua; + /* votes must be cast before configuring software control */ vote(chg->pl_disable_votable, PL_INDIRECT_VOTER, true, 0); diff --git a/drivers/power/qcom-charger/smb-lib.c b/drivers/power/qcom-charger/smb-lib.c index e9c189ae17e7..0067ec5c2ca2 100644 --- a/drivers/power/qcom-charger/smb-lib.c +++ b/drivers/power/qcom-charger/smb-lib.c @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -642,6 +643,31 @@ suspend: return rc; } +#define MICRO_250MA 250000 +static int smblib_otg_cl_config(struct smb_charger *chg, int otg_cl_ua) +{ + int rc = 0; + + rc = smblib_set_charge_param(chg, &chg->param.otg_cl, otg_cl_ua); + if (rc < 0) { + dev_err(chg->dev, "Couldn't set otg current limit rc=%d\n", + rc); + return rc; + } + + /* configure PFM/PWM mode for OTG regulator */ + rc = smblib_masked_write(chg, DC_ENG_SSUPPLY_CFG3_REG, + ENG_SSUPPLY_CFG_SKIP_TH_V0P2_BIT, + otg_cl_ua > MICRO_250MA ? 1 : 0); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't write DC_ENG_SSUPPLY_CFG3_REG rc=%d\n", rc); + return rc; + } + + return rc; +} + static int smblib_dc_icl_vote_callback(struct votable *votable, void *data, int icl_ua, const char *client) { @@ -746,14 +772,36 @@ static int smblib_pl_enable_indirect_vote_callback(struct votable *votable, * OTG REGULATOR * *****************/ +#define OTG_SOFT_START_DELAY_MS 20 int smblib_vbus_regulator_enable(struct regulator_dev *rdev) { struct smb_charger *chg = rdev_get_drvdata(rdev); + u8 stat; int rc = 0; - rc = regmap_write(chg->regmap, CMD_OTG_REG, OTG_EN_BIT); - if (rc < 0) + rc = smblib_masked_write(chg, OTG_ENG_OTG_CFG_REG, + ENG_BUCKBOOST_HALT1_8_MODE_BIT, + ENG_BUCKBOOST_HALT1_8_MODE_BIT); + if (rc < 0) { + dev_err(chg->dev, "Couldn't set OTG_ENG_OTG_CFG_REG rc=%d\n", + rc); + return rc; + } + + rc = smblib_write(chg, CMD_OTG_REG, OTG_EN_BIT); + if (rc < 0) { dev_err(chg->dev, "Couldn't enable OTG regulator rc=%d\n", rc); + return rc; + } + + msleep(OTG_SOFT_START_DELAY_MS); + rc = smblib_read(chg, OTG_STATUS_REG, &stat); + if (rc < 0) { + dev_err(chg->dev, "Couldn't read OTG_STATUS_REG rc=%d\n", rc); + return rc; + } + if (stat & BOOST_SOFTSTART_DONE_BIT) + smblib_otg_cl_config(chg, chg->otg_cl_ua); return rc; } @@ -763,9 +811,22 @@ int smblib_vbus_regulator_disable(struct regulator_dev *rdev) struct smb_charger *chg = rdev_get_drvdata(rdev); int rc = 0; - rc = regmap_write(chg->regmap, CMD_OTG_REG, 0); - if (rc < 0) + rc = smblib_write(chg, CMD_OTG_REG, 0); + if (rc < 0) { dev_err(chg->dev, "Couldn't disable OTG regulator rc=%d\n", rc); + return rc; + } + + smblib_otg_cl_config(chg, MICRO_250MA); + + rc = smblib_masked_write(chg, OTG_ENG_OTG_CFG_REG, + ENG_BUCKBOOST_HALT1_8_MODE_BIT, 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't set OTG_ENG_OTG_CFG_REG rc=%d\n", + rc); + return rc; + } + return rc; } diff --git a/drivers/power/qcom-charger/smb-lib.h b/drivers/power/qcom-charger/smb-lib.h index c9732c25dfcd..5b4c2016adc8 100644 --- a/drivers/power/qcom-charger/smb-lib.h +++ b/drivers/power/qcom-charger/smb-lib.h @@ -77,6 +77,7 @@ struct smb_params { struct smb_chg_param fv; struct smb_chg_param usb_icl; struct smb_chg_param icl_stat; + struct smb_chg_param otg_cl; struct smb_chg_param dc_icl; struct smb_chg_param dc_icl_pt_lv; struct smb_chg_param dc_icl_pt_hv; @@ -167,6 +168,8 @@ struct smb_charger { int thermal_levels; int *thermal_mitigation; + int otg_cl_ua; + int fake_capacity; bool step_chg_enabled; diff --git a/drivers/power/qcom-charger/smb-reg.h b/drivers/power/qcom-charger/smb-reg.h index c4ad72e254f9..8a49a8fb38ba 100644 --- a/drivers/power/qcom-charger/smb-reg.h +++ b/drivers/power/qcom-charger/smb-reg.h @@ -366,6 +366,9 @@ enum { #define OTG_EN_SRC_CFG_BIT BIT(1) #define CONCURRENT_MODE_CFG_BIT BIT(0) +#define OTG_ENG_OTG_CFG_REG (OTG_BASE + 0xC0) +#define ENG_BUCKBOOST_HALT1_8_MODE_BIT BIT(0) + /* BATIF Peripheral Registers */ /* BATIF Interrupt Bits */ #define BAT_7_RT_STS_BIT BIT(7) @@ -766,6 +769,13 @@ enum { ZIN_ICL_HV_MAX_MV = 11000, }; +#define DC_ENG_SSUPPLY_CFG3_REG (DCIN_BASE + 0xC2) +#define ENG_SSUPPLY_HI_CAP_BIT BIT(6) +#define ENG_SSUPPLY_HI_RES_BIT BIT(5) +#define ENG_SSUPPLY_CFG_SKIP_TH_V0P2_BIT BIT(3) +#define ENG_SSUPPLY_CFG_SYSOV_TH_4P8_BIT BIT(2) +#define ENG_SSUPPLY_5V_OV_OPT_BIT BIT(0) + /* MISC Peripheral Registers */ #define REVISION1_REG (MISC_BASE + 0x00) #define DIG_MINOR_MASK GENMASK(7, 0)