Loading drivers/power/supply/qcom/qpnp-smb5.c +211 −0 Original line number Original line Diff line number Diff line Loading @@ -1678,6 +1678,209 @@ static int smb5_init_batt_psy(struct smb5 *chip) return rc; return rc; } } /******************************** * DUAL-ROLE CLASS REGISTRATION * ********************************/ static enum dual_role_property smb5_dr_properties[] = { DUAL_ROLE_PROP_SUPPORTED_MODES, DUAL_ROLE_PROP_MODE, DUAL_ROLE_PROP_PR, DUAL_ROLE_PROP_DR, }; static int smb5_dr_get_property(struct dual_role_phy_instance *dual_role, enum dual_role_property prop, unsigned int *val) { struct smb_charger *chg = dual_role_get_drvdata(dual_role); union power_supply_propval pval = {0, }; /* Initializing pr, dr and mode to value 2 corresponding to NONE case */ int mode = 2, pr = 2, dr = 2, rc = 0; if (!chg) return -ENODEV; rc = smblib_get_prop_usb_present(chg, &pval); if (rc < 0) { pr_err("Couldn't get usb present status, rc=%d\n", rc); return rc; } if (chg->connector_type == POWER_SUPPLY_CONNECTOR_TYPEC) { if (chg->typec_mode == POWER_SUPPLY_TYPEC_NONE) { mode = DUAL_ROLE_PROP_MODE_NONE; pr = DUAL_ROLE_PROP_PR_NONE; dr = DUAL_ROLE_PROP_DR_NONE; } else if (chg->typec_mode < POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) { mode = DUAL_ROLE_PROP_MODE_DFP; pr = DUAL_ROLE_PROP_PR_SRC; dr = DUAL_ROLE_PROP_DR_HOST; } else if (chg->typec_mode >= POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) { mode = DUAL_ROLE_PROP_MODE_UFP; pr = DUAL_ROLE_PROP_PR_SNK; dr = DUAL_ROLE_PROP_DR_DEVICE; } } else if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) { pr = DUAL_ROLE_PROP_PR_NONE; if (chg->otg_present) { mode = DUAL_ROLE_PROP_MODE_DFP; dr = DUAL_ROLE_PROP_DR_HOST; } else if (pval.intval) { mode = DUAL_ROLE_PROP_MODE_UFP; dr = DUAL_ROLE_PROP_DR_DEVICE; } else { mode = DUAL_ROLE_PROP_MODE_NONE; dr = DUAL_ROLE_PROP_DR_NONE; } } switch (prop) { case DUAL_ROLE_PROP_MODE: *val = mode; break; case DUAL_ROLE_PROP_PR: *val = pr; break; case DUAL_ROLE_PROP_DR: *val = dr; break; default: pr_err("dual role class get property %d not supported\n", prop); return -EINVAL; } return 0; } static int smb5_dr_set_property(struct dual_role_phy_instance *dual_role, enum dual_role_property prop, const unsigned int *val) { struct smb_charger *chg = dual_role_get_drvdata(dual_role); int rc = 0; if (!chg) return -ENODEV; mutex_lock(&chg->dr_lock); switch (prop) { case DUAL_ROLE_PROP_MODE: if (chg->pr_swap_in_progress) { pr_debug("Already in mode transition. Skipping request\n"); mutex_unlock(&chg->dr_lock); return 0; } switch (*val) { case DUAL_ROLE_PROP_MODE_UFP: if (chg->typec_mode >= POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) { chg->dr_mode = DUAL_ROLE_PROP_MODE_UFP; } else { chg->pr_swap_in_progress = true; rc = smblib_force_dr_mode(chg, DUAL_ROLE_PROP_MODE_UFP); if (rc < 0) { chg->pr_swap_in_progress = false; pr_err("Failed to force UFP mode, rc=%d\n", rc); } } break; case DUAL_ROLE_PROP_MODE_DFP: if (chg->typec_mode < POWER_SUPPLY_TYPEC_SOURCE_DEFAULT && chg->typec_mode != POWER_SUPPLY_TYPEC_NONE) { chg->dr_mode = DUAL_ROLE_PROP_MODE_DFP; } else { chg->pr_swap_in_progress = true; rc = smblib_force_dr_mode(chg, DUAL_ROLE_PROP_MODE_DFP); if (rc < 0) { chg->pr_swap_in_progress = false; pr_err("Failed to force DFP mode, rc=%d\n", rc); } } break; default: pr_err("Invalid role (not DFP/UFP): %d\n", *val); rc = -EINVAL; } /* * Schedule delayed work to check if the device latched to * the requested mode. */ if (chg->pr_swap_in_progress && !rc) { cancel_delayed_work_sync(&chg->role_reversal_check); vote(chg->awake_votable, DR_SWAP_VOTER, true, 0); schedule_delayed_work(&chg->role_reversal_check, msecs_to_jiffies(ROLE_REVERSAL_DELAY_MS)); } break; default: pr_err("dual role class set property %d not supported\n", prop); rc = -EINVAL; } mutex_unlock(&chg->dr_lock); return rc; } static int smb5_dr_prop_writeable(struct dual_role_phy_instance *dual_role, enum dual_role_property prop) { struct smb_charger *chg = dual_role_get_drvdata(dual_role); if (!chg) return -ENODEV; /* uUSB connector does not support role switch */ if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) return 0; switch (prop) { case DUAL_ROLE_PROP_MODE: return 1; default: break; } return 0; } static const struct dual_role_phy_desc dr_desc = { .name = "otg_default", .supported_modes = DUAL_ROLE_SUPPORTED_MODES_DFP_AND_UFP, .properties = smb5_dr_properties, .num_properties = ARRAY_SIZE(smb5_dr_properties), .get_property = smb5_dr_get_property, .set_property = smb5_dr_set_property, .property_is_writeable = smb5_dr_prop_writeable, }; static int smb5_init_dual_role_class(struct smb5 *chip) { struct smb_charger *chg = &chip->chg; int rc = 0; /* Register dual role class for only non-PD TypeC and uUSB designs */ if (!chg->pd_not_supported) return rc; mutex_init(&chg->dr_lock); chg->dual_role = devm_dual_role_instance_register(chg->dev, &dr_desc); if (IS_ERR(chg->dual_role)) { pr_err("Couldn't register dual role class\n"); rc = PTR_ERR(chg->dual_role); } else { chg->dual_role->drv_data = chg; } return rc; } /****************************** /****************************** * VBUS REGULATOR REGISTRATION * * VBUS REGULATOR REGISTRATION * ******************************/ ******************************/ Loading Loading @@ -3224,6 +3427,14 @@ static int smb5_probe(struct platform_device *pdev) goto cleanup; goto cleanup; } } /* Register android dual-role class */ rc = smb5_init_dual_role_class(chip); if (rc < 0) { pr_err("Couldn't initialize dual role class, rc=%d\n", rc); goto cleanup; } rc = smb5_determine_initial_status(chip); rc = smb5_determine_initial_status(chip); if (rc < 0) { if (rc < 0) { pr_err("Couldn't determine initial status rc=%d\n", pr_err("Couldn't determine initial status rc=%d\n", Loading drivers/power/supply/qcom/smb5-lib.c +112 −0 Original line number Original line Diff line number Diff line Loading @@ -4718,6 +4718,8 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) smblib_micro_usb_plugin(chg, vbus_rising); smblib_micro_usb_plugin(chg, vbus_rising); power_supply_changed(chg->usb_psy); power_supply_changed(chg->usb_psy); if (chg->dual_role) dual_role_instance_changed(chg->dual_role); smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n", smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n", vbus_rising ? "attached" : "detached"); vbus_rising ? "attached" : "detached"); } } Loading Loading @@ -4957,6 +4959,8 @@ irqreturn_t usb_source_change_irq_handler(int irq, void *data) smblib_hvdcp_adaptive_voltage_change(chg); smblib_hvdcp_adaptive_voltage_change(chg); power_supply_changed(chg->usb_psy); power_supply_changed(chg->usb_psy); if (chg->dual_role) dual_role_instance_changed(chg->dual_role); rc = smblib_read(chg, APSD_STATUS_REG, &stat); rc = smblib_read(chg, APSD_STATUS_REG, &stat); if (rc < 0) { if (rc < 0) { Loading Loading @@ -5358,6 +5362,8 @@ irqreturn_t typec_state_change_irq_handler(int irq, void *data) smblib_typec_mode_name[chg->typec_mode]); smblib_typec_mode_name[chg->typec_mode]); power_supply_changed(chg->usb_psy); power_supply_changed(chg->usb_psy); if (chg->dual_role) dual_role_instance_changed(chg->dual_role); return IRQ_HANDLED; return IRQ_HANDLED; } } Loading Loading @@ -5434,6 +5440,15 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data) chg->sink_src_mode = UNATTACHED_MODE; chg->sink_src_mode = UNATTACHED_MODE; chg->early_usb_attach = false; chg->early_usb_attach = false; smblib_apsd_enable(chg, true); smblib_apsd_enable(chg, true); /* * Restore DRP mode on type-C cable disconnect if role * swap is not in progress, to ensure forced sink or src * mode configuration is reset properly. */ if (chg->dual_role) smblib_force_dr_mode(chg, DUAL_ROLE_PROP_MODE_NONE); } } if (chg->lpd_stage == LPD_STAGE_FLOAT_CANCEL) if (chg->lpd_stage == LPD_STAGE_FLOAT_CANCEL) Loading @@ -5442,6 +5457,8 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data) } } power_supply_changed(chg->usb_psy); power_supply_changed(chg->usb_psy); if (chg->dual_role) dual_role_instance_changed(chg->dual_role); return IRQ_HANDLED; return IRQ_HANDLED; } } Loading Loading @@ -6331,6 +6348,97 @@ static void smblib_lpd_detach_work(struct work_struct *work) chg->lpd_stage = LPD_STAGE_NONE; chg->lpd_stage = LPD_STAGE_NONE; } } static char *dr_mode_text[] = { "ufp", "dfp", "none" }; int smblib_force_dr_mode(struct smb_charger *chg, int mode) { int rc = 0; switch (mode) { case DUAL_ROLE_PROP_MODE_UFP: rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG, TYPEC_POWER_ROLE_CMD_MASK | EN_TRY_SNK_BIT, EN_SNK_ONLY_BIT); if (rc < 0) { smblib_err(chg, "Couldn't enable snk, rc=%d\n", rc); return rc; } break; case DUAL_ROLE_PROP_MODE_DFP: rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG, TYPEC_POWER_ROLE_CMD_MASK | EN_TRY_SNK_BIT, EN_SRC_ONLY_BIT); if (rc < 0) { smblib_err(chg, "Couldn't enable src, rc=%d\n", rc); return rc; } break; case DUAL_ROLE_PROP_MODE_NONE: rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG, TYPEC_POWER_ROLE_CMD_MASK | EN_TRY_SNK_BIT, EN_TRY_SNK_BIT); if (rc < 0) { smblib_err(chg, "Couldn't enable try.snk, rc=%d\n", rc); return rc; } break; default: smblib_err(chg, "Power role %d not supported\n", mode); return -EINVAL; } if (chg->dr_mode != mode) { chg->dr_mode = mode; smblib_dbg(chg, PR_MISC, "Forced mode: %s\n", dr_mode_text[chg->dr_mode]); } return rc; } static void smblib_dual_role_check_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, role_reversal_check.work); int rc = 0; mutex_lock(&chg->dr_lock); switch (chg->dr_mode) { case DUAL_ROLE_PROP_MODE_UFP: if (chg->typec_mode < POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) { smblib_dbg(chg, PR_MISC, "Role reversal not latched to UFP in %d msecs. Resetting to DRP mode\n", ROLE_REVERSAL_DELAY_MS); rc = smblib_force_dr_mode(chg, DUAL_ROLE_PROP_MODE_NONE); if (rc < 0) pr_err("Failed to set DRP mode, rc=%d\n", rc); } chg->pr_swap_in_progress = false; break; case DUAL_ROLE_PROP_MODE_DFP: if (chg->typec_mode >= POWER_SUPPLY_TYPEC_SOURCE_DEFAULT || chg->typec_mode == POWER_SUPPLY_TYPEC_NONE) { smblib_dbg(chg, PR_MISC, "Role reversal not latched to DFP in %d msecs. Resetting to DRP mode\n", ROLE_REVERSAL_DELAY_MS); rc = smblib_force_dr_mode(chg, DUAL_ROLE_PROP_MODE_NONE); if (rc < 0) pr_err("Failed to set DRP mode, rc=%d\n", rc); } chg->pr_swap_in_progress = false; break; default: pr_debug("Already in DRP mode\n"); break; } mutex_unlock(&chg->dr_lock); vote(chg->awake_votable, DR_SWAP_VOTER, false, 0); } static int smblib_create_votables(struct smb_charger *chg) static int smblib_create_votables(struct smb_charger *chg) { { int rc = 0; int rc = 0; Loading Loading @@ -6476,6 +6584,8 @@ int smblib_init(struct smb_charger *chg) INIT_DELAYED_WORK(&chg->thermal_regulation_work, INIT_DELAYED_WORK(&chg->thermal_regulation_work, smblib_thermal_regulation_work); smblib_thermal_regulation_work); INIT_DELAYED_WORK(&chg->usbov_dbc_work, smblib_usbov_dbc_work); INIT_DELAYED_WORK(&chg->usbov_dbc_work, smblib_usbov_dbc_work); INIT_DELAYED_WORK(&chg->role_reversal_check, smblib_dual_role_check_work); if (chg->wa_flags & CHG_TERMINATION_WA) { if (chg->wa_flags & CHG_TERMINATION_WA) { INIT_WORK(&chg->chg_termination_work, INIT_WORK(&chg->chg_termination_work, Loading Loading @@ -6511,6 +6621,7 @@ int smblib_init(struct smb_charger *chg) chg->sec_chg_selected = POWER_SUPPLY_CHARGER_SEC_NONE; chg->sec_chg_selected = POWER_SUPPLY_CHARGER_SEC_NONE; chg->cp_reason = POWER_SUPPLY_CP_NONE; chg->cp_reason = POWER_SUPPLY_CP_NONE; chg->thermal_status = TEMP_BELOW_RANGE; chg->thermal_status = TEMP_BELOW_RANGE; chg->dr_mode = DUAL_ROLE_PROP_MODE_NONE; switch (chg->mode) { switch (chg->mode) { case PARALLEL_MASTER: case PARALLEL_MASTER: Loading Loading @@ -6615,6 +6726,7 @@ int smblib_deinit(struct smb_charger *chg) cancel_delayed_work_sync(&chg->lpd_detach_work); cancel_delayed_work_sync(&chg->lpd_detach_work); cancel_delayed_work_sync(&chg->thermal_regulation_work); cancel_delayed_work_sync(&chg->thermal_regulation_work); cancel_delayed_work_sync(&chg->usbov_dbc_work); cancel_delayed_work_sync(&chg->usbov_dbc_work); cancel_delayed_work_sync(&chg->role_reversal_check); power_supply_unreg_notifier(&chg->nb); power_supply_unreg_notifier(&chg->nb); smblib_destroy_votables(chg); smblib_destroy_votables(chg); qcom_step_chg_deinit(); qcom_step_chg_deinit(); Loading drivers/power/supply/qcom/smb5-lib.h +11 −0 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <linux/regulator/driver.h> #include <linux/regulator/driver.h> #include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h> #include <linux/extcon.h> #include <linux/extcon.h> #include <linux/usb/class-dual-role.h> #include "storm-watch.h" #include "storm-watch.h" enum print_reason { enum print_reason { Loading Loading @@ -75,6 +76,7 @@ enum print_reason { #define CHG_TERMINATION_VOTER "CHG_TERMINATION_VOTER" #define CHG_TERMINATION_VOTER "CHG_TERMINATION_VOTER" #define THERMAL_THROTTLE_VOTER "THERMAL_THROTTLE_VOTER" #define THERMAL_THROTTLE_VOTER "THERMAL_THROTTLE_VOTER" #define VOUT_VOTER "VOUT_VOTER" #define VOUT_VOTER "VOUT_VOTER" #define DR_SWAP_VOTER "DR_SWAP_VOTER" #define BOOST_BACK_STORM_COUNT 3 #define BOOST_BACK_STORM_COUNT 3 #define WEAK_CHG_STORM_COUNT 8 #define WEAK_CHG_STORM_COUNT 8 Loading @@ -94,6 +96,8 @@ enum print_reason { #define TYPEC_MEDIUM_CURRENT_UA 1500000 #define TYPEC_MEDIUM_CURRENT_UA 1500000 #define TYPEC_HIGH_CURRENT_UA 3000000 #define TYPEC_HIGH_CURRENT_UA 3000000 #define ROLE_REVERSAL_DELAY_MS 2000 enum smb_mode { enum smb_mode { PARALLEL_MASTER = 0, PARALLEL_MASTER = 0, PARALLEL_SLAVE, PARALLEL_SLAVE, Loading Loading @@ -366,6 +370,7 @@ struct smb_charger { /* locks */ /* locks */ struct mutex smb_lock; struct mutex smb_lock; struct mutex ps_change_lock; struct mutex ps_change_lock; struct mutex dr_lock; /* power supplies */ /* power supplies */ struct power_supply *batt_psy; struct power_supply *batt_psy; Loading @@ -378,6 +383,9 @@ struct smb_charger { struct power_supply *cp_psy; struct power_supply *cp_psy; enum power_supply_type real_charger_type; enum power_supply_type real_charger_type; /* dual role class */ struct dual_role_phy_instance *dual_role; /* notifiers */ /* notifiers */ struct notifier_block nb; struct notifier_block nb; Loading Loading @@ -418,6 +426,7 @@ struct smb_charger { struct delayed_work lpd_detach_work; struct delayed_work lpd_detach_work; struct delayed_work thermal_regulation_work; struct delayed_work thermal_regulation_work; struct delayed_work usbov_dbc_work; struct delayed_work usbov_dbc_work; struct delayed_work role_reversal_check; struct alarm lpd_recheck_timer; struct alarm lpd_recheck_timer; struct alarm moisture_protection_alarm; struct alarm moisture_protection_alarm; Loading Loading @@ -503,6 +512,7 @@ struct smb_charger { bool aicl_max_reached; bool aicl_max_reached; int charge_full_cc; int charge_full_cc; int cc_soc_ref; int cc_soc_ref; int dr_mode; /* workaround flag */ /* workaround flag */ u32 wa_flags; u32 wa_flags; Loading Loading @@ -707,6 +717,7 @@ int smblib_get_prop_pr_swap_in_progress(struct smb_charger *chg, union power_supply_propval *val); union power_supply_propval *val); int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg, int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg, const union power_supply_propval *val); const union power_supply_propval *val); int smblib_force_dr_mode(struct smb_charger *chg, int mode); int smblib_get_prop_from_bms(struct smb_charger *chg, int smblib_get_prop_from_bms(struct smb_charger *chg, enum power_supply_property psp, enum power_supply_property psp, union power_supply_propval *val); union power_supply_propval *val); Loading Loading
drivers/power/supply/qcom/qpnp-smb5.c +211 −0 Original line number Original line Diff line number Diff line Loading @@ -1678,6 +1678,209 @@ static int smb5_init_batt_psy(struct smb5 *chip) return rc; return rc; } } /******************************** * DUAL-ROLE CLASS REGISTRATION * ********************************/ static enum dual_role_property smb5_dr_properties[] = { DUAL_ROLE_PROP_SUPPORTED_MODES, DUAL_ROLE_PROP_MODE, DUAL_ROLE_PROP_PR, DUAL_ROLE_PROP_DR, }; static int smb5_dr_get_property(struct dual_role_phy_instance *dual_role, enum dual_role_property prop, unsigned int *val) { struct smb_charger *chg = dual_role_get_drvdata(dual_role); union power_supply_propval pval = {0, }; /* Initializing pr, dr and mode to value 2 corresponding to NONE case */ int mode = 2, pr = 2, dr = 2, rc = 0; if (!chg) return -ENODEV; rc = smblib_get_prop_usb_present(chg, &pval); if (rc < 0) { pr_err("Couldn't get usb present status, rc=%d\n", rc); return rc; } if (chg->connector_type == POWER_SUPPLY_CONNECTOR_TYPEC) { if (chg->typec_mode == POWER_SUPPLY_TYPEC_NONE) { mode = DUAL_ROLE_PROP_MODE_NONE; pr = DUAL_ROLE_PROP_PR_NONE; dr = DUAL_ROLE_PROP_DR_NONE; } else if (chg->typec_mode < POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) { mode = DUAL_ROLE_PROP_MODE_DFP; pr = DUAL_ROLE_PROP_PR_SRC; dr = DUAL_ROLE_PROP_DR_HOST; } else if (chg->typec_mode >= POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) { mode = DUAL_ROLE_PROP_MODE_UFP; pr = DUAL_ROLE_PROP_PR_SNK; dr = DUAL_ROLE_PROP_DR_DEVICE; } } else if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) { pr = DUAL_ROLE_PROP_PR_NONE; if (chg->otg_present) { mode = DUAL_ROLE_PROP_MODE_DFP; dr = DUAL_ROLE_PROP_DR_HOST; } else if (pval.intval) { mode = DUAL_ROLE_PROP_MODE_UFP; dr = DUAL_ROLE_PROP_DR_DEVICE; } else { mode = DUAL_ROLE_PROP_MODE_NONE; dr = DUAL_ROLE_PROP_DR_NONE; } } switch (prop) { case DUAL_ROLE_PROP_MODE: *val = mode; break; case DUAL_ROLE_PROP_PR: *val = pr; break; case DUAL_ROLE_PROP_DR: *val = dr; break; default: pr_err("dual role class get property %d not supported\n", prop); return -EINVAL; } return 0; } static int smb5_dr_set_property(struct dual_role_phy_instance *dual_role, enum dual_role_property prop, const unsigned int *val) { struct smb_charger *chg = dual_role_get_drvdata(dual_role); int rc = 0; if (!chg) return -ENODEV; mutex_lock(&chg->dr_lock); switch (prop) { case DUAL_ROLE_PROP_MODE: if (chg->pr_swap_in_progress) { pr_debug("Already in mode transition. Skipping request\n"); mutex_unlock(&chg->dr_lock); return 0; } switch (*val) { case DUAL_ROLE_PROP_MODE_UFP: if (chg->typec_mode >= POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) { chg->dr_mode = DUAL_ROLE_PROP_MODE_UFP; } else { chg->pr_swap_in_progress = true; rc = smblib_force_dr_mode(chg, DUAL_ROLE_PROP_MODE_UFP); if (rc < 0) { chg->pr_swap_in_progress = false; pr_err("Failed to force UFP mode, rc=%d\n", rc); } } break; case DUAL_ROLE_PROP_MODE_DFP: if (chg->typec_mode < POWER_SUPPLY_TYPEC_SOURCE_DEFAULT && chg->typec_mode != POWER_SUPPLY_TYPEC_NONE) { chg->dr_mode = DUAL_ROLE_PROP_MODE_DFP; } else { chg->pr_swap_in_progress = true; rc = smblib_force_dr_mode(chg, DUAL_ROLE_PROP_MODE_DFP); if (rc < 0) { chg->pr_swap_in_progress = false; pr_err("Failed to force DFP mode, rc=%d\n", rc); } } break; default: pr_err("Invalid role (not DFP/UFP): %d\n", *val); rc = -EINVAL; } /* * Schedule delayed work to check if the device latched to * the requested mode. */ if (chg->pr_swap_in_progress && !rc) { cancel_delayed_work_sync(&chg->role_reversal_check); vote(chg->awake_votable, DR_SWAP_VOTER, true, 0); schedule_delayed_work(&chg->role_reversal_check, msecs_to_jiffies(ROLE_REVERSAL_DELAY_MS)); } break; default: pr_err("dual role class set property %d not supported\n", prop); rc = -EINVAL; } mutex_unlock(&chg->dr_lock); return rc; } static int smb5_dr_prop_writeable(struct dual_role_phy_instance *dual_role, enum dual_role_property prop) { struct smb_charger *chg = dual_role_get_drvdata(dual_role); if (!chg) return -ENODEV; /* uUSB connector does not support role switch */ if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) return 0; switch (prop) { case DUAL_ROLE_PROP_MODE: return 1; default: break; } return 0; } static const struct dual_role_phy_desc dr_desc = { .name = "otg_default", .supported_modes = DUAL_ROLE_SUPPORTED_MODES_DFP_AND_UFP, .properties = smb5_dr_properties, .num_properties = ARRAY_SIZE(smb5_dr_properties), .get_property = smb5_dr_get_property, .set_property = smb5_dr_set_property, .property_is_writeable = smb5_dr_prop_writeable, }; static int smb5_init_dual_role_class(struct smb5 *chip) { struct smb_charger *chg = &chip->chg; int rc = 0; /* Register dual role class for only non-PD TypeC and uUSB designs */ if (!chg->pd_not_supported) return rc; mutex_init(&chg->dr_lock); chg->dual_role = devm_dual_role_instance_register(chg->dev, &dr_desc); if (IS_ERR(chg->dual_role)) { pr_err("Couldn't register dual role class\n"); rc = PTR_ERR(chg->dual_role); } else { chg->dual_role->drv_data = chg; } return rc; } /****************************** /****************************** * VBUS REGULATOR REGISTRATION * * VBUS REGULATOR REGISTRATION * ******************************/ ******************************/ Loading Loading @@ -3224,6 +3427,14 @@ static int smb5_probe(struct platform_device *pdev) goto cleanup; goto cleanup; } } /* Register android dual-role class */ rc = smb5_init_dual_role_class(chip); if (rc < 0) { pr_err("Couldn't initialize dual role class, rc=%d\n", rc); goto cleanup; } rc = smb5_determine_initial_status(chip); rc = smb5_determine_initial_status(chip); if (rc < 0) { if (rc < 0) { pr_err("Couldn't determine initial status rc=%d\n", pr_err("Couldn't determine initial status rc=%d\n", Loading
drivers/power/supply/qcom/smb5-lib.c +112 −0 Original line number Original line Diff line number Diff line Loading @@ -4718,6 +4718,8 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) smblib_micro_usb_plugin(chg, vbus_rising); smblib_micro_usb_plugin(chg, vbus_rising); power_supply_changed(chg->usb_psy); power_supply_changed(chg->usb_psy); if (chg->dual_role) dual_role_instance_changed(chg->dual_role); smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n", smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n", vbus_rising ? "attached" : "detached"); vbus_rising ? "attached" : "detached"); } } Loading Loading @@ -4957,6 +4959,8 @@ irqreturn_t usb_source_change_irq_handler(int irq, void *data) smblib_hvdcp_adaptive_voltage_change(chg); smblib_hvdcp_adaptive_voltage_change(chg); power_supply_changed(chg->usb_psy); power_supply_changed(chg->usb_psy); if (chg->dual_role) dual_role_instance_changed(chg->dual_role); rc = smblib_read(chg, APSD_STATUS_REG, &stat); rc = smblib_read(chg, APSD_STATUS_REG, &stat); if (rc < 0) { if (rc < 0) { Loading Loading @@ -5358,6 +5362,8 @@ irqreturn_t typec_state_change_irq_handler(int irq, void *data) smblib_typec_mode_name[chg->typec_mode]); smblib_typec_mode_name[chg->typec_mode]); power_supply_changed(chg->usb_psy); power_supply_changed(chg->usb_psy); if (chg->dual_role) dual_role_instance_changed(chg->dual_role); return IRQ_HANDLED; return IRQ_HANDLED; } } Loading Loading @@ -5434,6 +5440,15 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data) chg->sink_src_mode = UNATTACHED_MODE; chg->sink_src_mode = UNATTACHED_MODE; chg->early_usb_attach = false; chg->early_usb_attach = false; smblib_apsd_enable(chg, true); smblib_apsd_enable(chg, true); /* * Restore DRP mode on type-C cable disconnect if role * swap is not in progress, to ensure forced sink or src * mode configuration is reset properly. */ if (chg->dual_role) smblib_force_dr_mode(chg, DUAL_ROLE_PROP_MODE_NONE); } } if (chg->lpd_stage == LPD_STAGE_FLOAT_CANCEL) if (chg->lpd_stage == LPD_STAGE_FLOAT_CANCEL) Loading @@ -5442,6 +5457,8 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data) } } power_supply_changed(chg->usb_psy); power_supply_changed(chg->usb_psy); if (chg->dual_role) dual_role_instance_changed(chg->dual_role); return IRQ_HANDLED; return IRQ_HANDLED; } } Loading Loading @@ -6331,6 +6348,97 @@ static void smblib_lpd_detach_work(struct work_struct *work) chg->lpd_stage = LPD_STAGE_NONE; chg->lpd_stage = LPD_STAGE_NONE; } } static char *dr_mode_text[] = { "ufp", "dfp", "none" }; int smblib_force_dr_mode(struct smb_charger *chg, int mode) { int rc = 0; switch (mode) { case DUAL_ROLE_PROP_MODE_UFP: rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG, TYPEC_POWER_ROLE_CMD_MASK | EN_TRY_SNK_BIT, EN_SNK_ONLY_BIT); if (rc < 0) { smblib_err(chg, "Couldn't enable snk, rc=%d\n", rc); return rc; } break; case DUAL_ROLE_PROP_MODE_DFP: rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG, TYPEC_POWER_ROLE_CMD_MASK | EN_TRY_SNK_BIT, EN_SRC_ONLY_BIT); if (rc < 0) { smblib_err(chg, "Couldn't enable src, rc=%d\n", rc); return rc; } break; case DUAL_ROLE_PROP_MODE_NONE: rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG, TYPEC_POWER_ROLE_CMD_MASK | EN_TRY_SNK_BIT, EN_TRY_SNK_BIT); if (rc < 0) { smblib_err(chg, "Couldn't enable try.snk, rc=%d\n", rc); return rc; } break; default: smblib_err(chg, "Power role %d not supported\n", mode); return -EINVAL; } if (chg->dr_mode != mode) { chg->dr_mode = mode; smblib_dbg(chg, PR_MISC, "Forced mode: %s\n", dr_mode_text[chg->dr_mode]); } return rc; } static void smblib_dual_role_check_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, role_reversal_check.work); int rc = 0; mutex_lock(&chg->dr_lock); switch (chg->dr_mode) { case DUAL_ROLE_PROP_MODE_UFP: if (chg->typec_mode < POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) { smblib_dbg(chg, PR_MISC, "Role reversal not latched to UFP in %d msecs. Resetting to DRP mode\n", ROLE_REVERSAL_DELAY_MS); rc = smblib_force_dr_mode(chg, DUAL_ROLE_PROP_MODE_NONE); if (rc < 0) pr_err("Failed to set DRP mode, rc=%d\n", rc); } chg->pr_swap_in_progress = false; break; case DUAL_ROLE_PROP_MODE_DFP: if (chg->typec_mode >= POWER_SUPPLY_TYPEC_SOURCE_DEFAULT || chg->typec_mode == POWER_SUPPLY_TYPEC_NONE) { smblib_dbg(chg, PR_MISC, "Role reversal not latched to DFP in %d msecs. Resetting to DRP mode\n", ROLE_REVERSAL_DELAY_MS); rc = smblib_force_dr_mode(chg, DUAL_ROLE_PROP_MODE_NONE); if (rc < 0) pr_err("Failed to set DRP mode, rc=%d\n", rc); } chg->pr_swap_in_progress = false; break; default: pr_debug("Already in DRP mode\n"); break; } mutex_unlock(&chg->dr_lock); vote(chg->awake_votable, DR_SWAP_VOTER, false, 0); } static int smblib_create_votables(struct smb_charger *chg) static int smblib_create_votables(struct smb_charger *chg) { { int rc = 0; int rc = 0; Loading Loading @@ -6476,6 +6584,8 @@ int smblib_init(struct smb_charger *chg) INIT_DELAYED_WORK(&chg->thermal_regulation_work, INIT_DELAYED_WORK(&chg->thermal_regulation_work, smblib_thermal_regulation_work); smblib_thermal_regulation_work); INIT_DELAYED_WORK(&chg->usbov_dbc_work, smblib_usbov_dbc_work); INIT_DELAYED_WORK(&chg->usbov_dbc_work, smblib_usbov_dbc_work); INIT_DELAYED_WORK(&chg->role_reversal_check, smblib_dual_role_check_work); if (chg->wa_flags & CHG_TERMINATION_WA) { if (chg->wa_flags & CHG_TERMINATION_WA) { INIT_WORK(&chg->chg_termination_work, INIT_WORK(&chg->chg_termination_work, Loading Loading @@ -6511,6 +6621,7 @@ int smblib_init(struct smb_charger *chg) chg->sec_chg_selected = POWER_SUPPLY_CHARGER_SEC_NONE; chg->sec_chg_selected = POWER_SUPPLY_CHARGER_SEC_NONE; chg->cp_reason = POWER_SUPPLY_CP_NONE; chg->cp_reason = POWER_SUPPLY_CP_NONE; chg->thermal_status = TEMP_BELOW_RANGE; chg->thermal_status = TEMP_BELOW_RANGE; chg->dr_mode = DUAL_ROLE_PROP_MODE_NONE; switch (chg->mode) { switch (chg->mode) { case PARALLEL_MASTER: case PARALLEL_MASTER: Loading Loading @@ -6615,6 +6726,7 @@ int smblib_deinit(struct smb_charger *chg) cancel_delayed_work_sync(&chg->lpd_detach_work); cancel_delayed_work_sync(&chg->lpd_detach_work); cancel_delayed_work_sync(&chg->thermal_regulation_work); cancel_delayed_work_sync(&chg->thermal_regulation_work); cancel_delayed_work_sync(&chg->usbov_dbc_work); cancel_delayed_work_sync(&chg->usbov_dbc_work); cancel_delayed_work_sync(&chg->role_reversal_check); power_supply_unreg_notifier(&chg->nb); power_supply_unreg_notifier(&chg->nb); smblib_destroy_votables(chg); smblib_destroy_votables(chg); qcom_step_chg_deinit(); qcom_step_chg_deinit(); Loading
drivers/power/supply/qcom/smb5-lib.h +11 −0 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,7 @@ #include <linux/regulator/driver.h> #include <linux/regulator/driver.h> #include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h> #include <linux/extcon.h> #include <linux/extcon.h> #include <linux/usb/class-dual-role.h> #include "storm-watch.h" #include "storm-watch.h" enum print_reason { enum print_reason { Loading Loading @@ -75,6 +76,7 @@ enum print_reason { #define CHG_TERMINATION_VOTER "CHG_TERMINATION_VOTER" #define CHG_TERMINATION_VOTER "CHG_TERMINATION_VOTER" #define THERMAL_THROTTLE_VOTER "THERMAL_THROTTLE_VOTER" #define THERMAL_THROTTLE_VOTER "THERMAL_THROTTLE_VOTER" #define VOUT_VOTER "VOUT_VOTER" #define VOUT_VOTER "VOUT_VOTER" #define DR_SWAP_VOTER "DR_SWAP_VOTER" #define BOOST_BACK_STORM_COUNT 3 #define BOOST_BACK_STORM_COUNT 3 #define WEAK_CHG_STORM_COUNT 8 #define WEAK_CHG_STORM_COUNT 8 Loading @@ -94,6 +96,8 @@ enum print_reason { #define TYPEC_MEDIUM_CURRENT_UA 1500000 #define TYPEC_MEDIUM_CURRENT_UA 1500000 #define TYPEC_HIGH_CURRENT_UA 3000000 #define TYPEC_HIGH_CURRENT_UA 3000000 #define ROLE_REVERSAL_DELAY_MS 2000 enum smb_mode { enum smb_mode { PARALLEL_MASTER = 0, PARALLEL_MASTER = 0, PARALLEL_SLAVE, PARALLEL_SLAVE, Loading Loading @@ -366,6 +370,7 @@ struct smb_charger { /* locks */ /* locks */ struct mutex smb_lock; struct mutex smb_lock; struct mutex ps_change_lock; struct mutex ps_change_lock; struct mutex dr_lock; /* power supplies */ /* power supplies */ struct power_supply *batt_psy; struct power_supply *batt_psy; Loading @@ -378,6 +383,9 @@ struct smb_charger { struct power_supply *cp_psy; struct power_supply *cp_psy; enum power_supply_type real_charger_type; enum power_supply_type real_charger_type; /* dual role class */ struct dual_role_phy_instance *dual_role; /* notifiers */ /* notifiers */ struct notifier_block nb; struct notifier_block nb; Loading Loading @@ -418,6 +426,7 @@ struct smb_charger { struct delayed_work lpd_detach_work; struct delayed_work lpd_detach_work; struct delayed_work thermal_regulation_work; struct delayed_work thermal_regulation_work; struct delayed_work usbov_dbc_work; struct delayed_work usbov_dbc_work; struct delayed_work role_reversal_check; struct alarm lpd_recheck_timer; struct alarm lpd_recheck_timer; struct alarm moisture_protection_alarm; struct alarm moisture_protection_alarm; Loading Loading @@ -503,6 +512,7 @@ struct smb_charger { bool aicl_max_reached; bool aicl_max_reached; int charge_full_cc; int charge_full_cc; int cc_soc_ref; int cc_soc_ref; int dr_mode; /* workaround flag */ /* workaround flag */ u32 wa_flags; u32 wa_flags; Loading Loading @@ -707,6 +717,7 @@ int smblib_get_prop_pr_swap_in_progress(struct smb_charger *chg, union power_supply_propval *val); union power_supply_propval *val); int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg, int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg, const union power_supply_propval *val); const union power_supply_propval *val); int smblib_force_dr_mode(struct smb_charger *chg, int mode); int smblib_get_prop_from_bms(struct smb_charger *chg, int smblib_get_prop_from_bms(struct smb_charger *chg, enum power_supply_property psp, enum power_supply_property psp, union power_supply_propval *val); union power_supply_propval *val); Loading