diff --git a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt index 6ead870baf38..dc868bfacf84 100644 --- a/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt +++ b/Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb2.txt @@ -210,6 +210,11 @@ Charger specific properties: Definition: Boolean flag which when present enables stepwise change in FCC. The default stepping rate is 100mA/sec. +- qcom,ufp-only-mode + Usage: optional + Value type: bool + Definition: Boolean flag which when present configure charger in SINK only + mode. ============================================= Second Level Nodes - SMB2 Charger Peripherals diff --git a/drivers/power/supply/qcom/qpnp-smb2.c b/drivers/power/supply/qcom/qpnp-smb2.c index d4833c57f924..7337aa7570b0 100644 --- a/drivers/power/supply/qcom/qpnp-smb2.c +++ b/drivers/power/supply/qcom/qpnp-smb2.c @@ -339,6 +339,9 @@ static int smb2_parse_dt(struct smb2 *chip) chg->fcc_stepper_enable = of_property_read_bool(node, "qcom,fcc-stepping-enable"); + chg->ufp_only_mode = of_property_read_bool(node, + "qcom,ufp-only-mode"); + return 0; } @@ -1891,6 +1894,7 @@ static int smb2_post_init(struct smb2 *chip) { struct smb_charger *chg = &chip->chg; int rc; + u8 stat; /* In case the usb path is suspended, we would have missed disabling * the icl change interrupt because the interrupt could have been @@ -1898,13 +1902,36 @@ static int smb2_post_init(struct smb2 *chip) */ rerun_election(chg->usb_icl_votable); - /* configure power role for dual-role */ - rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, - TYPEC_POWER_ROLE_CMD_MASK, 0); - if (rc < 0) { - dev_err(chg->dev, - "Couldn't configure power role for DRP rc=%d\n", rc); - return rc; + /* Force charger in Sink Only mode */ + if (chg->ufp_only_mode) { + rc = smblib_read(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + &stat); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read SOFTWARE_CTRL_REG rc=%d\n", rc); + return rc; + } + + if (!(stat & UFP_EN_CMD_BIT)) { + /* configure charger in UFP only mode */ + rc = smblib_force_ufp(chg); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't force UFP mode rc=%d\n", rc); + return rc; + } + } + } else { + /* configure power role for dual-role */ + rc = smblib_masked_write(chg, + TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_POWER_ROLE_CMD_MASK, 0); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't configure power role for DRP rc=%d\n", + rc); + return rc; + } } rerun_election(chg->usb_irq_enable_votable); @@ -2568,8 +2595,9 @@ static void smb2_shutdown(struct platform_device *pdev) /* disable all interrupts */ smb2_disable_interrupts(chg); - /* configure power role for UFP */ - smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + if (!chg->ufp_only_mode) + /* configure power role for UFP */ + smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, TYPEC_POWER_ROLE_CMD_MASK, UFP_EN_CMD_BIT); /* force HVDCP to 5V */ diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c index 08ab434cef43..fc087b100301 100644 --- a/drivers/power/supply/qcom/smb-lib.c +++ b/drivers/power/supply/qcom/smb-lib.c @@ -506,6 +506,43 @@ static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg, /******************** * HELPER FUNCTIONS * ********************/ + +int smblib_force_ufp(struct smb_charger *chg) +{ + int rc; + + /* force FSM in IDLE state */ + rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_DISABLE_CMD_BIT, TYPEC_DISABLE_CMD_BIT); + if (rc < 0) { + smblib_err(chg, "Couldn't put FSM in idle rc=%d\n", rc); + return rc; + } + + /* wait for FSM to enter idle state */ + msleep(200); + + rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + VCONN_EN_VALUE_BIT | UFP_EN_CMD_BIT, UFP_EN_CMD_BIT); + if (rc < 0) { + smblib_err(chg, "Couldn't force UFP mode rc=%d\n", rc); + return rc; + } + + /* wait for mode change before enabling FSM */ + usleep_range(10000, 11000); + + /* release FSM from idle state */ + rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, + TYPEC_DISABLE_CMD_BIT, 0); + if (rc < 0) { + smblib_err(chg, "Couldn't release FSM from idle rc=%d\n", rc); + return rc; + } + + return 0; +} + static int smblib_request_dpdm(struct smb_charger *chg, bool enable) { int rc = 0; @@ -1077,6 +1114,11 @@ static int __smblib_set_prop_typec_power_role(struct smb_charger *chg, return -EINVAL; } + if (power_role != TYPEC_DISABLE_CMD_BIT) { + if (chg->ufp_only_mode) + power_role = UFP_EN_CMD_BIT; + } + if (chg->wa_flags & TYPEC_PBS_WA_BIT) { if (power_role == UFP_EN_CMD_BIT) { /* disable PBS workaround when forcing sink mode */ @@ -1096,7 +1138,7 @@ static int __smblib_set_prop_typec_power_role(struct smb_charger *chg, } rc = smblib_masked_write(chg, TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG, - TYPEC_POWER_ROLE_CMD_MASK, power_role); + TYPEC_POWER_ROLE_CMD_MASK, power_role); if (rc < 0) { smblib_err(chg, "Couldn't write 0x%02x to TYPE_C_INTRPT_ENB_SOFTWARE_CTRL rc=%d\n", power_role, rc); @@ -5227,6 +5269,8 @@ static int smblib_create_votables(struct smb_charger *chg) rc = PTR_ERR(chg->disable_power_role_switch); return rc; } + vote(chg->disable_power_role_switch, DEFAULT_VOTER, + chg->ufp_only_mode, 0); return rc; } diff --git a/drivers/power/supply/qcom/smb-lib.h b/drivers/power/supply/qcom/smb-lib.h index 28cddb2b5b2a..3babdac9d322 100644 --- a/drivers/power/supply/qcom/smb-lib.h +++ b/drivers/power/supply/qcom/smb-lib.h @@ -356,6 +356,7 @@ struct smb_charger { bool otg_present; bool disable_stat_sw_override; bool fcc_stepper_enable; + bool ufp_only_mode; /* workaround flag */ u32 wa_flags; @@ -548,6 +549,7 @@ int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg, int smblib_stat_sw_override_cfg(struct smb_charger *chg, bool override); void smblib_usb_typec_change(struct smb_charger *chg); int smblib_toggle_stat(struct smb_charger *chg, int reset); +int smblib_force_ufp(struct smb_charger *chg); int smblib_init(struct smb_charger *chg); int smblib_deinit(struct smb_charger *chg);