Loading Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt +0 −6 Original line number Diff line number Diff line Loading @@ -114,12 +114,6 @@ Charger specific properties: SOC. If this property is not specified, then auto recharge will be based off battery voltage. - qcom,micro-usb Usage: optional Value type: <empty> Definition: Boolean flag which indicates that the platform only support micro usb port. - qcom,suspend-input-on-debug-batt Usage: optional Value type: <empty> Loading drivers/power/supply/qcom/qpnp-smb5.c +225 −37 Original line number Diff line number Diff line Loading @@ -28,7 +28,67 @@ #include "smb5-reg.h" #include "smb5-lib.h" static struct smb_params smb5_params = { static struct smb_params smb5_pmi632_params = { .fcc = { .name = "fast charge current", .reg = CHGR_FAST_CHARGE_CURRENT_CFG_REG, .min_u = 0, .max_u = 3000000, .step_u = 50000, }, .fv = { .name = "float voltage", .reg = CHGR_FLOAT_VOLTAGE_CFG_REG, .min_u = 3600000, .max_u = 4800000, .step_u = 10000, }, .usb_icl = { .name = "usb input current limit", .reg = USBIN_CURRENT_LIMIT_CFG_REG, .min_u = 0, .max_u = 3000000, .step_u = 50000, }, .icl_stat = { .name = "input current limit status", .reg = AICL_ICL_STATUS_REG, .min_u = 0, .max_u = 3000000, .step_u = 50000, }, .otg_cl = { .name = "usb otg current limit", .reg = DCDC_OTG_CURRENT_LIMIT_CFG_REG, .min_u = 500000, .max_u = 1000000, .step_u = 250000, }, .jeita_cc_comp_hot = { .name = "jeita fcc reduction", .reg = JEITA_CCCOMP_CFG_HOT_REG, .min_u = 0, .max_u = 1575000, .step_u = 25000, }, .jeita_cc_comp_cold = { .name = "jeita fcc reduction", .reg = JEITA_CCCOMP_CFG_COLD_REG, .min_u = 0, .max_u = 1575000, .step_u = 25000, }, .freq_switcher = { .name = "switching frequency", .reg = DCDC_FSW_SEL_REG, .min_u = 600, .max_u = 1200, .step_u = 400, .set_proc = smblib_set_chg_freq, }, }; static struct smb_params smb5_pmi855_params = { .fcc = { .name = "fast charge current", .reg = CHGR_FAST_CHARGE_CURRENT_CFG_REG, Loading Loading @@ -119,6 +179,64 @@ module_param_named( weak_chg_icl_ua, __weak_chg_icl_ua, int, 0600 ); #define PMI632_MAX_ICL_UA 3000000 static int smb5_chg_config_init(struct smb5 *chip) { struct smb_charger *chg = &chip->chg; struct pmic_revid_data *pmic_rev_id; struct device_node *revid_dev_node; int rc = 0; revid_dev_node = of_parse_phandle(chip->chg.dev->of_node, "qcom,pmic-revid", 0); if (!revid_dev_node) { pr_err("Missing qcom,pmic-revid property\n"); return -EINVAL; } pmic_rev_id = get_revid_data(revid_dev_node); if (IS_ERR_OR_NULL(pmic_rev_id)) { /* * the revid peripheral must be registered, any failure * here only indicates that the rev-id module has not * probed yet. */ rc = -EPROBE_DEFER; goto out; } switch (pmic_rev_id->pmic_subtype) { case PM855B_SUBTYPE: chip->chg.smb_version = PM855B_SUBTYPE; chg->param = smb5_pmi855_params; chg->name = "pm855b_charger"; break; case PMI632_SUBTYPE: chip->chg.smb_version = PMI632_SUBTYPE; chg->param = smb5_pmi632_params; chg->use_extcon = true; chg->name = "pmi632_charger"; chg->hw_max_icl_ua = (chip->dt.usb_icl_ua > 0) ? chip->dt.usb_icl_ua : PMI632_MAX_ICL_UA; chg->chg_freq.freq_5V = 600; chg->chg_freq.freq_6V_8V = 800; chg->chg_freq.freq_9V = 1050; chg->chg_freq.freq_removal = 1050; chg->chg_freq.freq_below_otg_threshold = 800; chg->chg_freq.freq_above_otg_threshold = 800; break; default: pr_err("PMIC subtype %d not supported\n", pmic_rev_id->pmic_subtype); rc = -EINVAL; } out: of_node_put(revid_dev_node); return rc; } #define MICRO_1P5A 1500000 #define MICRO_P1A 100000 #define OTG_DEFAULT_DEGLITCH_TIME_MS 50 Loading Loading @@ -213,8 +331,6 @@ static int smb5_parse_dt(struct smb5 *chip) chip->dt.auto_recharge_soc = of_property_read_bool(node, "qcom,auto-recharge-soc"); chg->micro_usb_mode = of_property_read_bool(node, "qcom,micro-usb"); chg->dcp_icl_ua = chip->dt.usb_icl_ua; chg->suspend_input_on_debug_batt = of_property_read_bool(node, Loading Loading @@ -253,6 +369,7 @@ static enum power_supply_property smb5_usb_props[] = { POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, POWER_SUPPLY_PROP_PD_VOLTAGE_MIN, POWER_SUPPLY_PROP_SDP_CURRENT_MAX, POWER_SUPPLY_PROP_CONNECTOR_TYPE, }; static int smb5_usb_get_prop(struct power_supply *psy, Loading @@ -272,12 +389,13 @@ static int smb5_usb_get_prop(struct power_supply *psy, if (!val->intval) break; if ((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT || chg->micro_usb_mode) && chg->real_charger_type == POWER_SUPPLY_TYPE_USB) if (((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) || (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)) && (chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) val->intval = 0; else val->intval = 1; if (chg->real_charger_type == POWER_SUPPLY_TYPE_UNKNOWN) val->intval = 0; break; Loading @@ -297,19 +415,19 @@ static int smb5_usb_get_prop(struct power_supply *psy, val->intval = chg->real_charger_type; break; case POWER_SUPPLY_PROP_TYPEC_MODE: if (chg->micro_usb_mode) if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) val->intval = POWER_SUPPLY_TYPEC_NONE; else val->intval = chg->typec_mode; break; case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE: if (chg->micro_usb_mode) if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) val->intval = POWER_SUPPLY_TYPEC_PR_NONE; else rc = smblib_get_prop_typec_power_role(chg, val); break; case POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION: if (chg->micro_usb_mode) if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) val->intval = 0; else rc = smblib_get_prop_typec_cc_orientation(chg, val); Loading Loading @@ -354,6 +472,9 @@ static int smb5_usb_get_prop(struct power_supply *psy, val->intval = get_client_vote(chg->usb_icl_votable, USB_PSY_VOTER); break; case POWER_SUPPLY_PROP_CONNECTOR_TYPE: val->intval = chg->connector_type; break; default: pr_err("get prop %d is not supported in usb\n", psp); rc = -EINVAL; Loading Loading @@ -496,9 +617,9 @@ static int smb5_usb_port_get_prop(struct power_supply *psy, if (!val->intval) break; if ((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT || chg->micro_usb_mode) && chg->real_charger_type == POWER_SUPPLY_TYPE_USB) if (((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) || (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)) && (chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) val->intval = 1; else val->intval = 0; Loading Loading @@ -1093,7 +1214,7 @@ static int smb5_init_vconn_regulator(struct smb5 *chip) struct regulator_config cfg = {}; int rc = 0; if (chg->micro_usb_mode) if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) return 0; chg->vconn_vreg = devm_kzalloc(chg->dev, sizeof(*chg->vconn_vreg), Loading Loading @@ -1139,7 +1260,6 @@ static int smb5_configure_typec(struct smb_charger *chg) } rc = smblib_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG, MICRO_USB_STATE_CHANGE_INT_EN_BIT | TYPEC_WATER_DETECTION_INT_EN_BIT); if (rc < 0) { dev_err(chg->dev, Loading Loading @@ -1172,14 +1292,23 @@ static int smb5_configure_micro_usb(struct smb_charger *chg) return rc; } rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG, MICRO_USB_STATE_CHANGE_INT_EN_BIT, MICRO_USB_STATE_CHANGE_INT_EN_BIT); if (rc < 0) { dev_err(chg->dev, "Couldn't configure Type-C interrupts rc=%d\n", rc); return rc; } return rc; } static int smb5_init_hw(struct smb5 *chip) { struct smb_charger *chg = &chip->chg; int rc; u8 val; int rc, type = 0; u8 val = 0; if (chip->dt.no_battery) chg->fake_capacity = 50; Loading @@ -1194,8 +1323,50 @@ static int smb5_init_hw(struct smb5 *chip) smblib_get_charge_param(chg, &chg->param.usb_icl, &chg->default_icl_ua); if (chip->dt.usb_icl_ua < 0) chip->dt.usb_icl_ua = chg->default_icl_ua; /* Use SW based VBUS control, disable HW autonomous mode */ /* TODO: auth can be enabled through vote based on APSD flow */ rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG, HVDCP_AUTH_ALG_EN_CFG_BIT | HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT, HVDCP_AUTH_ALG_EN_CFG_BIT); if (rc < 0) { dev_err(chg->dev, "Couldn't configure HVDCP rc=%d\n", rc); return rc; } /* * PMI632 can have the connector type defined by a dedicated register * TYPEC_MICRO_USB_MODE_REG or by a common TYPEC_U_USB_CFG_REG. */ if (chg->smb_version == PMI632_SUBTYPE) { rc = smblib_read(chg, TYPEC_MICRO_USB_MODE_REG, &val); if (rc < 0) { dev_err(chg->dev, "Couldn't read USB mode rc=%d\n", rc); return rc; } type = !!(val & MICRO_USB_MODE_ONLY_BIT); } /* * If TYPEC_MICRO_USB_MODE_REG is not set and for all non-PMI632 * check the connector type using TYPEC_U_USB_CFG_REG. */ if (!type) { rc = smblib_read(chg, TYPEC_U_USB_CFG_REG, &val); if (rc < 0) { dev_err(chg->dev, "Couldn't read U_USB config rc=%d\n", rc); return rc; } type = !!(val & EN_MICRO_USB_MODE_BIT); } chg->connector_type = type ? POWER_SUPPLY_CONNECTOR_MICRO_USB : POWER_SUPPLY_CONNECTOR_TYPEC; pr_debug("Connector type=%s\n", type ? "Micro USB" : "TypeC"); smblib_rerun_apsd_if_required(chg); /* vote 0mA on usb_icl for non battery platforms */ vote(chg->usb_icl_votable, Loading @@ -1207,15 +1378,21 @@ static int smb5_init_hw(struct smb5 *chip) vote(chg->fv_votable, HW_LIMIT_VOTER, chip->dt.batt_profile_fv_uv > 0, chip->dt.batt_profile_fv_uv); vote(chg->fcc_votable, BATT_PROFILE_VOTER, true, chg->batt_profile_fcc_ua); BATT_PROFILE_VOTER, chg->batt_profile_fcc_ua > 0, chg->batt_profile_fcc_ua); vote(chg->fv_votable, BATT_PROFILE_VOTER, true, chg->batt_profile_fv_uv); BATT_PROFILE_VOTER, chg->batt_profile_fv_uv > 0, chg->batt_profile_fv_uv); vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0); vote(chg->pd_disallowed_votable_indirect, APSD_VOTER, true, 0); vote(chg->pd_disallowed_votable_indirect, MICRO_USB_VOTER, chg->micro_usb_mode, 0); chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB, 0); /* Some h/w limit maximum supported ICL */ vote(chg->usb_icl_votable, HW_LIMIT_VOTER, chg->hw_max_icl_ua > 0, chg->hw_max_icl_ua); /* * AICL configuration: Loading @@ -1235,7 +1412,7 @@ static int smb5_init_hw(struct smb5 *chip) return rc; } if (chg->micro_usb_mode) if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) rc = smb5_configure_micro_usb(chg); else rc = smb5_configure_typec(chg); Loading Loading @@ -1384,8 +1561,8 @@ static int smb5_post_init(struct smb5 *chip) pval.intval = POWER_SUPPLY_TYPEC_PR_DUAL; rc = smblib_set_prop_typec_power_role(chg, &pval); if (rc < 0) { dev_err(chg->dev, "Couldn't configure power role for DRP rc=%d\n", rc); dev_err(chg->dev, "Couldn't configure DRP role rc=%d\n", rc); return rc; } Loading @@ -1405,6 +1582,7 @@ static int smb5_determine_initial_status(struct smb5 *chip) if (chg->bms_psy) smblib_suspend_on_debug_battery(chg); usb_plugin_irq_handler(0, &irq_data); typec_state_change_irq_handler(0, &irq_data); usb_source_change_irq_handler(0, &irq_data); Loading @@ -1412,6 +1590,7 @@ static int smb5_determine_initial_status(struct smb5 *chip) icl_change_irq_handler(0, &irq_data); batt_temp_changed_irq_handler(0, &irq_data); wdog_bark_irq_handler(0, &irq_data); typec_or_rid_detection_change_irq_handler(0, &irq_data); return 0; } Loading Loading @@ -1586,7 +1765,7 @@ static struct smb_irq_info smb5_irqs[] = { /* TYPEC IRQs */ [TYPEC_OR_RID_DETECTION_CHANGE_IRQ] = { .name = "typec-or-rid-detect-change", .handler = default_irq_handler, .handler = typec_or_rid_detection_change_irq_handler, }, [TYPEC_VPD_DETECT_IRQ] = { .name = "typec-vpd-detect", Loading Loading @@ -1880,13 +2059,12 @@ static int smb5_probe(struct platform_device *pdev) chg = &chip->chg; chg->dev = &pdev->dev; chg->param = smb5_params; chg->debug_mask = &__debug_mask; chg->weak_chg_icl_ua = &__weak_chg_icl_ua; chg->mode = PARALLEL_MASTER; chg->irq_info = smb5_irqs; chg->die_health = -EINVAL; chg->name = "pm855b_charger"; chg->otg_present = false; chg->regmap = dev_get_regmap(chg->dev->parent, NULL); if (!chg->regmap) { Loading @@ -1897,13 +2075,20 @@ static int smb5_probe(struct platform_device *pdev) rc = smb5_parse_dt(chip); if (rc < 0) { pr_err("Couldn't parse device tree rc=%d\n", rc); goto cleanup; return rc; } rc = smb5_chg_config_init(chip); if (rc < 0) { if (rc != -EPROBE_DEFER) pr_err("Couldn't setup chg_config rc=%d\n", rc); return rc; } rc = smblib_init(chg); if (rc < 0) { pr_err("Smblib_init failed rc=%d\n", rc); goto cleanup; return rc; } /* set driver data before resources request it */ Loading Loading @@ -1945,11 +2130,13 @@ static int smb5_probe(struct platform_device *pdev) goto cleanup; } if (chg->smb_version == PM855B_SUBTYPE) { rc = smb5_init_dc_psy(chip); if (rc < 0) { pr_err("Couldn't initialize dc psy rc=%d\n", rc); goto cleanup; } } rc = smb5_init_usb_psy(chip); if (rc < 0) { Loading Loading @@ -1991,7 +2178,7 @@ static int smb5_probe(struct platform_device *pdev) rc = smb5_post_init(chip); if (rc < 0) { pr_err("Failed in post init rc=%d\n", rc); goto cleanup; goto free_irq; } smb5_create_debugfs(chip); Loading @@ -1999,7 +2186,7 @@ static int smb5_probe(struct platform_device *pdev) rc = smb5_show_charger_status(chip); if (rc < 0) { pr_err("Failed in getting charger status rc=%d\n", rc); goto cleanup; goto free_irq; } device_init_wakeup(chg->dev, true); Loading @@ -2008,8 +2195,9 @@ static int smb5_probe(struct platform_device *pdev) return rc; cleanup: free_irq: smb5_free_interrupts(chg); cleanup: smblib_deinit(chg); platform_set_drvdata(pdev, NULL); Loading @@ -2036,7 +2224,7 @@ static void smb5_shutdown(struct platform_device *pdev) smb5_disable_interrupts(chg); /* configure power role for UFP */ if (!chg->micro_usb_mode) if (chg->connector_type == POWER_SUPPLY_CONNECTOR_TYPEC) smblib_masked_write(chg, TYPE_C_MODE_CFG_REG, TYPEC_POWER_ROLE_CMD_MASK, EN_SNK_ONLY_BIT); Loading drivers/power/supply/qcom/smb5-lib.c +140 −105 Original line number Diff line number Diff line Loading @@ -107,19 +107,6 @@ int smblib_get_jeita_cc_delta(struct smb_charger *chg, int *cc_delta_ua) return 0; } int smblib_icl_override(struct smb_charger *chg, bool override) { int rc; rc = smblib_masked_write(chg, USBIN_LOAD_CFG_REG, ICL_OVERRIDE_AFTER_APSD_BIT, override ? ICL_OVERRIDE_AFTER_APSD_BIT : 0); if (rc < 0) smblib_err(chg, "Couldn't override ICL rc=%d\n", rc); return rc; } int smblib_stat_sw_override_cfg(struct smb_charger *chg, bool override) { int rc = 0; Loading @@ -137,6 +124,39 @@ int smblib_stat_sw_override_cfg(struct smb_charger *chg, bool override) return rc; } static void smblib_notify_extcon_props(struct smb_charger *chg, int id) { union extcon_property_value val; union power_supply_propval prop_val; if (chg->connector_type == POWER_SUPPLY_CONNECTOR_TYPEC) { smblib_get_prop_typec_cc_orientation(chg, &prop_val); val.intval = ((prop_val.intval == 2) ? 1 : 0); extcon_set_property(chg->extcon, id, EXTCON_PROP_USB_TYPEC_POLARITY, val); } val.intval = true; extcon_set_property(chg->extcon, id, EXTCON_PROP_USB_SS, val); } static void smblib_notify_device_mode(struct smb_charger *chg, bool enable) { if (enable) smblib_notify_extcon_props(chg, EXTCON_USB); extcon_set_state_sync(chg->extcon, EXTCON_USB, enable); } static void smblib_notify_usb_host(struct smb_charger *chg, bool enable) { if (enable) smblib_notify_extcon_props(chg, EXTCON_USB_HOST); extcon_set_state_sync(chg->extcon, EXTCON_USB_HOST, enable); } /******************** * REGISTER GETTERS * ********************/ Loading Loading @@ -281,6 +301,48 @@ static const struct apsd_result *smblib_get_apsd_result(struct smb_charger *chg) /******************** * REGISTER SETTERS * ********************/ static const struct buck_boost_freq chg_freq_list[] = { [0] = { .freq_khz = 2400, .val = 7, }, [1] = { .freq_khz = 2100, .val = 8, }, [2] = { .freq_khz = 1600, .val = 11, }, [3] = { .freq_khz = 1200, .val = 15, }, }; int smblib_set_chg_freq(struct smb_chg_param *param, int val_u, u8 *val_raw) { u8 i; if (val_u > param->max_u || val_u < param->min_u) return -EINVAL; /* Charger FSW is the configured freqency / 2 */ val_u *= 2; for (i = 0; i < ARRAY_SIZE(chg_freq_list); i++) { if (chg_freq_list[i].freq_khz == val_u) break; } if (i == ARRAY_SIZE(chg_freq_list)) { pr_err("Invalid frequency %d Hz\n", val_u / 2); return -EINVAL; } *val_raw = chg_freq_list[i].val; return 0; } int smblib_set_opt_switcher_freq(struct smb_charger *chg, int fsw_khz) { Loading Loading @@ -316,11 +378,15 @@ int smblib_set_charge_param(struct smb_charger *chg, if (rc < 0) return -EINVAL; } else { if (val_u > param->max_u || val_u < param->min_u) { smblib_err(chg, "%s: %d is out of range [%d, %d]\n", if (val_u > param->max_u || val_u < param->min_u) smblib_dbg(chg, PR_MISC, "%s: %d is out of range [%d, %d]\n", param->name, val_u, param->min_u, param->max_u); return -EINVAL; } if (val_u > param->max_u) val_u = param->max_u; if (val_u < param->min_u) val_u = param->min_u; val_raw = (val_u - param->min_u) / param->step_u; } Loading Loading @@ -771,69 +837,46 @@ static int get_sdp_current(struct smb_charger *chg, int *icl_ua) int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) { int rc = 0; bool override; bool hc_mode = false; /* suspend and return if 25mA or less is requested */ if (icl_ua <= USBIN_25MA) return smblib_set_usb_suspend(chg, true); if (icl_ua == INT_MAX) goto override_suspend_config; goto set_mode; /* configure current */ if (chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT if (((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) || (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)) && (chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) { rc = set_sdp_current(chg, icl_ua); if (rc < 0) { smblib_err(chg, "Couldn't set SDP ICL rc=%d\n", rc); goto enable_icl_changed_interrupt; goto out; } } else { set_sdp_current(chg, 100000); rc = smblib_set_charge_param(chg, &chg->param.usb_icl, icl_ua); if (rc < 0) { smblib_err(chg, "Couldn't set HC ICL rc=%d\n", rc); goto enable_icl_changed_interrupt; goto out; } hc_mode = true; } override_suspend_config: /* determine if override needs to be enforced */ override = true; if (icl_ua == INT_MAX) { /* remove override if no voters - hw defaults is desired */ override = false; } else if (chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) { if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB) /* For std cable with type = SDP never override */ override = false; else if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_CDP && icl_ua == 1500000) /* * For std cable with type = CDP override only if * current is not 1500mA */ override = false; } /* enforce override */ set_mode: rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG, USBIN_MODE_CHG_BIT, override ? USBIN_MODE_CHG_BIT : 0); rc = smblib_icl_override(chg, override); if (rc < 0) { smblib_err(chg, "Couldn't set ICL override rc=%d\n", rc); goto enable_icl_changed_interrupt; } USBIN_MODE_CHG_BIT, hc_mode ? USBIN_MODE_CHG_BIT : 0); /* unsuspend after configuring current and override */ rc = smblib_set_usb_suspend(chg, false); if (rc < 0) { smblib_err(chg, "Couldn't resume input rc=%d\n", rc); goto enable_icl_changed_interrupt; goto out; } enable_icl_changed_interrupt: out: return rc; } Loading @@ -844,7 +887,7 @@ int smblib_get_icl_current(struct smb_charger *chg, int *icl_ua) bool override; if ((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT || chg->micro_usb_mode) || chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) && (chg->usb_psy->desc->type == POWER_SUPPLY_TYPE_USB)) { rc = get_sdp_current(chg, icl_ua); if (rc < 0) { Loading Loading @@ -881,6 +924,9 @@ static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data, { struct smb_charger *chg = data; if (chg->smb_version == PMI632_SUBTYPE) return 0; /* resume input if suspend is invalid */ if (suspend < 0) suspend = 0; Loading Loading @@ -2076,6 +2122,9 @@ int smblib_set_prop_typec_power_role(struct smb_charger *chg, int rc = 0; u8 power_role; if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) return 0; switch (val->intval) { case POWER_SUPPLY_TYPEC_PR_NONE: power_role = TYPEC_DISABLE_CMD_BIT; Loading Loading @@ -2513,7 +2562,7 @@ static void smblib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising) } else { chg->typec_present = 0; smblib_update_usb_type(chg); extcon_set_state_sync(chg->extcon, EXTCON_USB, false); smblib_notify_device_mode(chg, false); smblib_uusb_removal(chg); } } Loading Loading @@ -2601,7 +2650,7 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc); } if (chg->micro_usb_mode) if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) smblib_micro_usb_plugin(chg, vbus_rising); power_supply_changed(chg->usb_psy); Loading Loading @@ -2763,37 +2812,6 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg, rising ? "rising" : "falling"); } static void smblib_notify_extcon_props(struct smb_charger *chg, int id) { union extcon_property_value val; union power_supply_propval prop_val; smblib_get_prop_typec_cc_orientation(chg, &prop_val); val.intval = ((prop_val.intval == 2) ? 1 : 0); extcon_set_property(chg->extcon, id, EXTCON_PROP_USB_TYPEC_POLARITY, val); val.intval = true; extcon_set_property(chg->extcon, id, EXTCON_PROP_USB_SS, val); } static void smblib_notify_device_mode(struct smb_charger *chg, bool enable) { if (enable) smblib_notify_extcon_props(chg, EXTCON_USB); extcon_set_state_sync(chg->extcon, EXTCON_USB, enable); } static void smblib_notify_usb_host(struct smb_charger *chg, bool enable) { if (enable) smblib_notify_extcon_props(chg, EXTCON_USB_HOST); extcon_set_state_sync(chg->extcon, EXTCON_USB_HOST, enable); } static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) { const struct apsd_result *apsd_result; Loading @@ -2806,13 +2824,11 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) switch (apsd_result->bit) { case SDP_CHARGER_BIT: case CDP_CHARGER_BIT: if (chg->micro_usb_mode) extcon_set_state_sync(chg->extcon, EXTCON_USB, true); /* if not DCP then no hvdcp timeout happens. Enable pd here */ vote(chg->pd_disallowed_votable_indirect, APSD_VOTER, false, 0); if (chg->use_extcon) if ((chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) || chg->use_extcon) smblib_notify_device_mode(chg, true); break; case OCP_CHARGER_BIT: Loading Loading @@ -2845,7 +2861,8 @@ irqreturn_t usb_source_change_irq_handler(int irq, void *data) } smblib_dbg(chg, PR_REGISTER, "APSD_STATUS = 0x%02x\n", stat); if (chg->micro_usb_mode && (stat & APSD_DTC_STATUS_DONE_BIT) if ((chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) && (stat & APSD_DTC_STATUS_DONE_BIT) && !chg->uusb_apsd_rerun_done) { /* * Force re-run APSD to handle slow insertion related Loading Loading @@ -3146,12 +3163,12 @@ static void smblib_usb_typec_change(struct smb_charger *chg) power_supply_changed(chg->usb_psy); } irqreturn_t typec_state_change_irq_handler(int irq, void *data) irqreturn_t typec_or_rid_detection_change_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; if (chg->micro_usb_mode) { if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) { cancel_delayed_work_sync(&chg->uusb_otg_work); vote(chg->awake_votable, OTG_DELAY_VOTER, true, 0); smblib_dbg(chg, PR_INTERRUPT, "Scheduling OTG work\n"); Loading @@ -3160,7 +3177,16 @@ irqreturn_t typec_state_change_irq_handler(int irq, void *data) return IRQ_HANDLED; } if (chg->pr_swap_in_progress) { return IRQ_HANDLED; } irqreturn_t typec_state_change_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; if ((chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) || chg->pr_swap_in_progress) { smblib_dbg(chg, PR_INTERRUPT, "Ignoring since pr_swap_in_progress\n"); return IRQ_HANDLED; Loading Loading @@ -3335,10 +3361,20 @@ static void smblib_uusb_otg_work(struct work_struct *work) smblib_err(chg, "Couldn't read TYPE_C_STATUS_3 rc=%d\n", rc); goto out; } otg = !!(stat & U_USB_GROUND_NOVBUS_BIT); if (chg->otg_present != otg) smblib_notify_usb_host(chg, otg); chg->otg_present = otg; if (!otg) chg->boost_current_ua = 0; otg = !!(stat & (U_USB_GROUND_NOVBUS_BIT | U_USB_GROUND_BIT)); extcon_set_state_sync(chg->extcon, EXTCON_USB_HOST, otg); smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_3 = 0x%02x OTG=%d\n", rc = smblib_set_charge_param(chg, &chg->param.freq_switcher, otg ? chg->chg_freq.freq_below_otg_threshold : chg->chg_freq.freq_removal); if (rc < 0) dev_err(chg->dev, "Error in setting freq_boost rc=%d\n", rc); smblib_dbg(chg, PR_REGISTER, "TYPE_C_U_USB_STATUS = 0x%02x OTG=%d\n", stat, otg); power_supply_changed(chg->usb_psy); Loading Loading @@ -3559,13 +3595,6 @@ int smblib_init(struct smb_charger *chg) return rc; } rc = smblib_register_notifier(chg); if (rc < 0) { smblib_err(chg, "Couldn't register notifier rc=%d\n", rc); return rc; } chg->bms_psy = power_supply_get_by_name("bms"); chg->pl.psy = power_supply_get_by_name("parallel"); if (chg->pl.psy) { Loading @@ -3576,6 +3605,12 @@ int smblib_init(struct smb_charger *chg) return rc; } } rc = smblib_register_notifier(chg); if (rc < 0) { smblib_err(chg, "Couldn't register notifier rc=%d\n", rc); return rc; } break; case PARALLEL_SLAVE: break; Loading drivers/power/supply/qcom/smb5-lib.h +10 −4 Original line number Diff line number Diff line Loading @@ -196,6 +196,11 @@ struct smb_chg_param { u8 *val_raw); }; struct buck_boost_freq { int freq_khz; u8 val; }; struct smb_chg_freq { unsigned int freq_5V; unsigned int freq_6V_8V; Loading Loading @@ -311,7 +316,7 @@ struct smb_charger { bool sw_jeita_enabled; bool is_hdc; bool chg_done; bool micro_usb_mode; int connector_type; bool otg_en; bool suspend_input_on_debug_batt; int otg_attempts; Loading @@ -329,6 +334,7 @@ struct smb_charger { u8 float_cfg; bool use_extcon; bool otg_present; int hw_max_icl_ua; /* workaround flag */ u32 wa_flags; Loading Loading @@ -369,6 +375,8 @@ int smblib_mapping_cc_delta_from_field_value(struct smb_chg_param *param, int val_u, u8 *val_raw); int smblib_set_chg_freq(struct smb_chg_param *param, int val_u, u8 *val_raw); int smblib_set_prop_boost_current(struct smb_charger *chg, const union power_supply_propval *val); int smblib_vbus_regulator_enable(struct regulator_dev *rdev); int smblib_vbus_regulator_disable(struct regulator_dev *rdev); int smblib_vbus_regulator_is_enabled(struct regulator_dev *rdev); Loading @@ -390,6 +398,7 @@ irqreturn_t dc_plugin_irq_handler(int irq, void *data); irqreturn_t high_duty_cycle_irq_handler(int irq, void *data); irqreturn_t switcher_power_ok_irq_handler(int irq, void *data); irqreturn_t wdog_bark_irq_handler(int irq, void *data); irqreturn_t typec_or_rid_detection_change_irq_handler(int irq, void *data); int smblib_get_prop_input_suspend(struct smb_charger *chg, union power_supply_propval *val); Loading Loading @@ -470,8 +479,6 @@ int smblib_set_prop_pd_voltage_max(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_pd_voltage_min(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_boost_current(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_typec_power_role(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_pd_active(struct smb_charger *chg, Loading @@ -484,7 +491,6 @@ void smblib_suspend_on_debug_battery(struct smb_charger *chg); int smblib_rerun_apsd_if_required(struct smb_charger *chg); int smblib_get_prop_fcc_delta(struct smb_charger *chg, union power_supply_propval *val); int smblib_icl_override(struct smb_charger *chg, bool override); int smblib_dp_dm(struct smb_charger *chg, int val); int smblib_disable_hw_jeita(struct smb_charger *chg, bool disable); int smblib_rerun_aicl(struct smb_charger *chg); Loading drivers/power/supply/qcom/smb5-reg.h +3 −0 Original line number Diff line number Diff line Loading @@ -196,6 +196,7 @@ enum { }; #define USBIN_OPTIONS_1_CFG_REG (USBIN_BASE + 0x62) #define HVDCP_AUTH_ALG_EN_CFG_BIT BIT(6) #define HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT BIT(5) #define BC1P2_SRC_DETECT_BIT BIT(3) Loading Loading @@ -299,6 +300,8 @@ enum { #define TYPEC_U_USB_CFG_REG (TYPEC_BASE + 0x70) #define EN_MICRO_USB_MODE_BIT BIT(0) #define TYPEC_MICRO_USB_MODE_REG (TYPEC_BASE + 0x70) #define MICRO_USB_MODE_ONLY_BIT BIT(0) /******************************** * MISC Peripheral Registers * ********************************/ Loading Loading
Documentation/devicetree/bindings/power/supply/qcom/qpnp-smb5.txt +0 −6 Original line number Diff line number Diff line Loading @@ -114,12 +114,6 @@ Charger specific properties: SOC. If this property is not specified, then auto recharge will be based off battery voltage. - qcom,micro-usb Usage: optional Value type: <empty> Definition: Boolean flag which indicates that the platform only support micro usb port. - qcom,suspend-input-on-debug-batt Usage: optional Value type: <empty> Loading
drivers/power/supply/qcom/qpnp-smb5.c +225 −37 Original line number Diff line number Diff line Loading @@ -28,7 +28,67 @@ #include "smb5-reg.h" #include "smb5-lib.h" static struct smb_params smb5_params = { static struct smb_params smb5_pmi632_params = { .fcc = { .name = "fast charge current", .reg = CHGR_FAST_CHARGE_CURRENT_CFG_REG, .min_u = 0, .max_u = 3000000, .step_u = 50000, }, .fv = { .name = "float voltage", .reg = CHGR_FLOAT_VOLTAGE_CFG_REG, .min_u = 3600000, .max_u = 4800000, .step_u = 10000, }, .usb_icl = { .name = "usb input current limit", .reg = USBIN_CURRENT_LIMIT_CFG_REG, .min_u = 0, .max_u = 3000000, .step_u = 50000, }, .icl_stat = { .name = "input current limit status", .reg = AICL_ICL_STATUS_REG, .min_u = 0, .max_u = 3000000, .step_u = 50000, }, .otg_cl = { .name = "usb otg current limit", .reg = DCDC_OTG_CURRENT_LIMIT_CFG_REG, .min_u = 500000, .max_u = 1000000, .step_u = 250000, }, .jeita_cc_comp_hot = { .name = "jeita fcc reduction", .reg = JEITA_CCCOMP_CFG_HOT_REG, .min_u = 0, .max_u = 1575000, .step_u = 25000, }, .jeita_cc_comp_cold = { .name = "jeita fcc reduction", .reg = JEITA_CCCOMP_CFG_COLD_REG, .min_u = 0, .max_u = 1575000, .step_u = 25000, }, .freq_switcher = { .name = "switching frequency", .reg = DCDC_FSW_SEL_REG, .min_u = 600, .max_u = 1200, .step_u = 400, .set_proc = smblib_set_chg_freq, }, }; static struct smb_params smb5_pmi855_params = { .fcc = { .name = "fast charge current", .reg = CHGR_FAST_CHARGE_CURRENT_CFG_REG, Loading Loading @@ -119,6 +179,64 @@ module_param_named( weak_chg_icl_ua, __weak_chg_icl_ua, int, 0600 ); #define PMI632_MAX_ICL_UA 3000000 static int smb5_chg_config_init(struct smb5 *chip) { struct smb_charger *chg = &chip->chg; struct pmic_revid_data *pmic_rev_id; struct device_node *revid_dev_node; int rc = 0; revid_dev_node = of_parse_phandle(chip->chg.dev->of_node, "qcom,pmic-revid", 0); if (!revid_dev_node) { pr_err("Missing qcom,pmic-revid property\n"); return -EINVAL; } pmic_rev_id = get_revid_data(revid_dev_node); if (IS_ERR_OR_NULL(pmic_rev_id)) { /* * the revid peripheral must be registered, any failure * here only indicates that the rev-id module has not * probed yet. */ rc = -EPROBE_DEFER; goto out; } switch (pmic_rev_id->pmic_subtype) { case PM855B_SUBTYPE: chip->chg.smb_version = PM855B_SUBTYPE; chg->param = smb5_pmi855_params; chg->name = "pm855b_charger"; break; case PMI632_SUBTYPE: chip->chg.smb_version = PMI632_SUBTYPE; chg->param = smb5_pmi632_params; chg->use_extcon = true; chg->name = "pmi632_charger"; chg->hw_max_icl_ua = (chip->dt.usb_icl_ua > 0) ? chip->dt.usb_icl_ua : PMI632_MAX_ICL_UA; chg->chg_freq.freq_5V = 600; chg->chg_freq.freq_6V_8V = 800; chg->chg_freq.freq_9V = 1050; chg->chg_freq.freq_removal = 1050; chg->chg_freq.freq_below_otg_threshold = 800; chg->chg_freq.freq_above_otg_threshold = 800; break; default: pr_err("PMIC subtype %d not supported\n", pmic_rev_id->pmic_subtype); rc = -EINVAL; } out: of_node_put(revid_dev_node); return rc; } #define MICRO_1P5A 1500000 #define MICRO_P1A 100000 #define OTG_DEFAULT_DEGLITCH_TIME_MS 50 Loading Loading @@ -213,8 +331,6 @@ static int smb5_parse_dt(struct smb5 *chip) chip->dt.auto_recharge_soc = of_property_read_bool(node, "qcom,auto-recharge-soc"); chg->micro_usb_mode = of_property_read_bool(node, "qcom,micro-usb"); chg->dcp_icl_ua = chip->dt.usb_icl_ua; chg->suspend_input_on_debug_batt = of_property_read_bool(node, Loading Loading @@ -253,6 +369,7 @@ static enum power_supply_property smb5_usb_props[] = { POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, POWER_SUPPLY_PROP_PD_VOLTAGE_MIN, POWER_SUPPLY_PROP_SDP_CURRENT_MAX, POWER_SUPPLY_PROP_CONNECTOR_TYPE, }; static int smb5_usb_get_prop(struct power_supply *psy, Loading @@ -272,12 +389,13 @@ static int smb5_usb_get_prop(struct power_supply *psy, if (!val->intval) break; if ((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT || chg->micro_usb_mode) && chg->real_charger_type == POWER_SUPPLY_TYPE_USB) if (((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) || (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)) && (chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) val->intval = 0; else val->intval = 1; if (chg->real_charger_type == POWER_SUPPLY_TYPE_UNKNOWN) val->intval = 0; break; Loading @@ -297,19 +415,19 @@ static int smb5_usb_get_prop(struct power_supply *psy, val->intval = chg->real_charger_type; break; case POWER_SUPPLY_PROP_TYPEC_MODE: if (chg->micro_usb_mode) if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) val->intval = POWER_SUPPLY_TYPEC_NONE; else val->intval = chg->typec_mode; break; case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE: if (chg->micro_usb_mode) if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) val->intval = POWER_SUPPLY_TYPEC_PR_NONE; else rc = smblib_get_prop_typec_power_role(chg, val); break; case POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION: if (chg->micro_usb_mode) if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) val->intval = 0; else rc = smblib_get_prop_typec_cc_orientation(chg, val); Loading Loading @@ -354,6 +472,9 @@ static int smb5_usb_get_prop(struct power_supply *psy, val->intval = get_client_vote(chg->usb_icl_votable, USB_PSY_VOTER); break; case POWER_SUPPLY_PROP_CONNECTOR_TYPE: val->intval = chg->connector_type; break; default: pr_err("get prop %d is not supported in usb\n", psp); rc = -EINVAL; Loading Loading @@ -496,9 +617,9 @@ static int smb5_usb_port_get_prop(struct power_supply *psy, if (!val->intval) break; if ((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT || chg->micro_usb_mode) && chg->real_charger_type == POWER_SUPPLY_TYPE_USB) if (((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) || (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)) && (chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) val->intval = 1; else val->intval = 0; Loading Loading @@ -1093,7 +1214,7 @@ static int smb5_init_vconn_regulator(struct smb5 *chip) struct regulator_config cfg = {}; int rc = 0; if (chg->micro_usb_mode) if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) return 0; chg->vconn_vreg = devm_kzalloc(chg->dev, sizeof(*chg->vconn_vreg), Loading Loading @@ -1139,7 +1260,6 @@ static int smb5_configure_typec(struct smb_charger *chg) } rc = smblib_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG, MICRO_USB_STATE_CHANGE_INT_EN_BIT | TYPEC_WATER_DETECTION_INT_EN_BIT); if (rc < 0) { dev_err(chg->dev, Loading Loading @@ -1172,14 +1292,23 @@ static int smb5_configure_micro_usb(struct smb_charger *chg) return rc; } rc = smblib_masked_write(chg, TYPE_C_INTERRUPT_EN_CFG_2_REG, MICRO_USB_STATE_CHANGE_INT_EN_BIT, MICRO_USB_STATE_CHANGE_INT_EN_BIT); if (rc < 0) { dev_err(chg->dev, "Couldn't configure Type-C interrupts rc=%d\n", rc); return rc; } return rc; } static int smb5_init_hw(struct smb5 *chip) { struct smb_charger *chg = &chip->chg; int rc; u8 val; int rc, type = 0; u8 val = 0; if (chip->dt.no_battery) chg->fake_capacity = 50; Loading @@ -1194,8 +1323,50 @@ static int smb5_init_hw(struct smb5 *chip) smblib_get_charge_param(chg, &chg->param.usb_icl, &chg->default_icl_ua); if (chip->dt.usb_icl_ua < 0) chip->dt.usb_icl_ua = chg->default_icl_ua; /* Use SW based VBUS control, disable HW autonomous mode */ /* TODO: auth can be enabled through vote based on APSD flow */ rc = smblib_masked_write(chg, USBIN_OPTIONS_1_CFG_REG, HVDCP_AUTH_ALG_EN_CFG_BIT | HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT, HVDCP_AUTH_ALG_EN_CFG_BIT); if (rc < 0) { dev_err(chg->dev, "Couldn't configure HVDCP rc=%d\n", rc); return rc; } /* * PMI632 can have the connector type defined by a dedicated register * TYPEC_MICRO_USB_MODE_REG or by a common TYPEC_U_USB_CFG_REG. */ if (chg->smb_version == PMI632_SUBTYPE) { rc = smblib_read(chg, TYPEC_MICRO_USB_MODE_REG, &val); if (rc < 0) { dev_err(chg->dev, "Couldn't read USB mode rc=%d\n", rc); return rc; } type = !!(val & MICRO_USB_MODE_ONLY_BIT); } /* * If TYPEC_MICRO_USB_MODE_REG is not set and for all non-PMI632 * check the connector type using TYPEC_U_USB_CFG_REG. */ if (!type) { rc = smblib_read(chg, TYPEC_U_USB_CFG_REG, &val); if (rc < 0) { dev_err(chg->dev, "Couldn't read U_USB config rc=%d\n", rc); return rc; } type = !!(val & EN_MICRO_USB_MODE_BIT); } chg->connector_type = type ? POWER_SUPPLY_CONNECTOR_MICRO_USB : POWER_SUPPLY_CONNECTOR_TYPEC; pr_debug("Connector type=%s\n", type ? "Micro USB" : "TypeC"); smblib_rerun_apsd_if_required(chg); /* vote 0mA on usb_icl for non battery platforms */ vote(chg->usb_icl_votable, Loading @@ -1207,15 +1378,21 @@ static int smb5_init_hw(struct smb5 *chip) vote(chg->fv_votable, HW_LIMIT_VOTER, chip->dt.batt_profile_fv_uv > 0, chip->dt.batt_profile_fv_uv); vote(chg->fcc_votable, BATT_PROFILE_VOTER, true, chg->batt_profile_fcc_ua); BATT_PROFILE_VOTER, chg->batt_profile_fcc_ua > 0, chg->batt_profile_fcc_ua); vote(chg->fv_votable, BATT_PROFILE_VOTER, true, chg->batt_profile_fv_uv); BATT_PROFILE_VOTER, chg->batt_profile_fv_uv > 0, chg->batt_profile_fv_uv); vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0); vote(chg->pd_disallowed_votable_indirect, APSD_VOTER, true, 0); vote(chg->pd_disallowed_votable_indirect, MICRO_USB_VOTER, chg->micro_usb_mode, 0); chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB, 0); /* Some h/w limit maximum supported ICL */ vote(chg->usb_icl_votable, HW_LIMIT_VOTER, chg->hw_max_icl_ua > 0, chg->hw_max_icl_ua); /* * AICL configuration: Loading @@ -1235,7 +1412,7 @@ static int smb5_init_hw(struct smb5 *chip) return rc; } if (chg->micro_usb_mode) if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) rc = smb5_configure_micro_usb(chg); else rc = smb5_configure_typec(chg); Loading Loading @@ -1384,8 +1561,8 @@ static int smb5_post_init(struct smb5 *chip) pval.intval = POWER_SUPPLY_TYPEC_PR_DUAL; rc = smblib_set_prop_typec_power_role(chg, &pval); if (rc < 0) { dev_err(chg->dev, "Couldn't configure power role for DRP rc=%d\n", rc); dev_err(chg->dev, "Couldn't configure DRP role rc=%d\n", rc); return rc; } Loading @@ -1405,6 +1582,7 @@ static int smb5_determine_initial_status(struct smb5 *chip) if (chg->bms_psy) smblib_suspend_on_debug_battery(chg); usb_plugin_irq_handler(0, &irq_data); typec_state_change_irq_handler(0, &irq_data); usb_source_change_irq_handler(0, &irq_data); Loading @@ -1412,6 +1590,7 @@ static int smb5_determine_initial_status(struct smb5 *chip) icl_change_irq_handler(0, &irq_data); batt_temp_changed_irq_handler(0, &irq_data); wdog_bark_irq_handler(0, &irq_data); typec_or_rid_detection_change_irq_handler(0, &irq_data); return 0; } Loading Loading @@ -1586,7 +1765,7 @@ static struct smb_irq_info smb5_irqs[] = { /* TYPEC IRQs */ [TYPEC_OR_RID_DETECTION_CHANGE_IRQ] = { .name = "typec-or-rid-detect-change", .handler = default_irq_handler, .handler = typec_or_rid_detection_change_irq_handler, }, [TYPEC_VPD_DETECT_IRQ] = { .name = "typec-vpd-detect", Loading Loading @@ -1880,13 +2059,12 @@ static int smb5_probe(struct platform_device *pdev) chg = &chip->chg; chg->dev = &pdev->dev; chg->param = smb5_params; chg->debug_mask = &__debug_mask; chg->weak_chg_icl_ua = &__weak_chg_icl_ua; chg->mode = PARALLEL_MASTER; chg->irq_info = smb5_irqs; chg->die_health = -EINVAL; chg->name = "pm855b_charger"; chg->otg_present = false; chg->regmap = dev_get_regmap(chg->dev->parent, NULL); if (!chg->regmap) { Loading @@ -1897,13 +2075,20 @@ static int smb5_probe(struct platform_device *pdev) rc = smb5_parse_dt(chip); if (rc < 0) { pr_err("Couldn't parse device tree rc=%d\n", rc); goto cleanup; return rc; } rc = smb5_chg_config_init(chip); if (rc < 0) { if (rc != -EPROBE_DEFER) pr_err("Couldn't setup chg_config rc=%d\n", rc); return rc; } rc = smblib_init(chg); if (rc < 0) { pr_err("Smblib_init failed rc=%d\n", rc); goto cleanup; return rc; } /* set driver data before resources request it */ Loading Loading @@ -1945,11 +2130,13 @@ static int smb5_probe(struct platform_device *pdev) goto cleanup; } if (chg->smb_version == PM855B_SUBTYPE) { rc = smb5_init_dc_psy(chip); if (rc < 0) { pr_err("Couldn't initialize dc psy rc=%d\n", rc); goto cleanup; } } rc = smb5_init_usb_psy(chip); if (rc < 0) { Loading Loading @@ -1991,7 +2178,7 @@ static int smb5_probe(struct platform_device *pdev) rc = smb5_post_init(chip); if (rc < 0) { pr_err("Failed in post init rc=%d\n", rc); goto cleanup; goto free_irq; } smb5_create_debugfs(chip); Loading @@ -1999,7 +2186,7 @@ static int smb5_probe(struct platform_device *pdev) rc = smb5_show_charger_status(chip); if (rc < 0) { pr_err("Failed in getting charger status rc=%d\n", rc); goto cleanup; goto free_irq; } device_init_wakeup(chg->dev, true); Loading @@ -2008,8 +2195,9 @@ static int smb5_probe(struct platform_device *pdev) return rc; cleanup: free_irq: smb5_free_interrupts(chg); cleanup: smblib_deinit(chg); platform_set_drvdata(pdev, NULL); Loading @@ -2036,7 +2224,7 @@ static void smb5_shutdown(struct platform_device *pdev) smb5_disable_interrupts(chg); /* configure power role for UFP */ if (!chg->micro_usb_mode) if (chg->connector_type == POWER_SUPPLY_CONNECTOR_TYPEC) smblib_masked_write(chg, TYPE_C_MODE_CFG_REG, TYPEC_POWER_ROLE_CMD_MASK, EN_SNK_ONLY_BIT); Loading
drivers/power/supply/qcom/smb5-lib.c +140 −105 Original line number Diff line number Diff line Loading @@ -107,19 +107,6 @@ int smblib_get_jeita_cc_delta(struct smb_charger *chg, int *cc_delta_ua) return 0; } int smblib_icl_override(struct smb_charger *chg, bool override) { int rc; rc = smblib_masked_write(chg, USBIN_LOAD_CFG_REG, ICL_OVERRIDE_AFTER_APSD_BIT, override ? ICL_OVERRIDE_AFTER_APSD_BIT : 0); if (rc < 0) smblib_err(chg, "Couldn't override ICL rc=%d\n", rc); return rc; } int smblib_stat_sw_override_cfg(struct smb_charger *chg, bool override) { int rc = 0; Loading @@ -137,6 +124,39 @@ int smblib_stat_sw_override_cfg(struct smb_charger *chg, bool override) return rc; } static void smblib_notify_extcon_props(struct smb_charger *chg, int id) { union extcon_property_value val; union power_supply_propval prop_val; if (chg->connector_type == POWER_SUPPLY_CONNECTOR_TYPEC) { smblib_get_prop_typec_cc_orientation(chg, &prop_val); val.intval = ((prop_val.intval == 2) ? 1 : 0); extcon_set_property(chg->extcon, id, EXTCON_PROP_USB_TYPEC_POLARITY, val); } val.intval = true; extcon_set_property(chg->extcon, id, EXTCON_PROP_USB_SS, val); } static void smblib_notify_device_mode(struct smb_charger *chg, bool enable) { if (enable) smblib_notify_extcon_props(chg, EXTCON_USB); extcon_set_state_sync(chg->extcon, EXTCON_USB, enable); } static void smblib_notify_usb_host(struct smb_charger *chg, bool enable) { if (enable) smblib_notify_extcon_props(chg, EXTCON_USB_HOST); extcon_set_state_sync(chg->extcon, EXTCON_USB_HOST, enable); } /******************** * REGISTER GETTERS * ********************/ Loading Loading @@ -281,6 +301,48 @@ static const struct apsd_result *smblib_get_apsd_result(struct smb_charger *chg) /******************** * REGISTER SETTERS * ********************/ static const struct buck_boost_freq chg_freq_list[] = { [0] = { .freq_khz = 2400, .val = 7, }, [1] = { .freq_khz = 2100, .val = 8, }, [2] = { .freq_khz = 1600, .val = 11, }, [3] = { .freq_khz = 1200, .val = 15, }, }; int smblib_set_chg_freq(struct smb_chg_param *param, int val_u, u8 *val_raw) { u8 i; if (val_u > param->max_u || val_u < param->min_u) return -EINVAL; /* Charger FSW is the configured freqency / 2 */ val_u *= 2; for (i = 0; i < ARRAY_SIZE(chg_freq_list); i++) { if (chg_freq_list[i].freq_khz == val_u) break; } if (i == ARRAY_SIZE(chg_freq_list)) { pr_err("Invalid frequency %d Hz\n", val_u / 2); return -EINVAL; } *val_raw = chg_freq_list[i].val; return 0; } int smblib_set_opt_switcher_freq(struct smb_charger *chg, int fsw_khz) { Loading Loading @@ -316,11 +378,15 @@ int smblib_set_charge_param(struct smb_charger *chg, if (rc < 0) return -EINVAL; } else { if (val_u > param->max_u || val_u < param->min_u) { smblib_err(chg, "%s: %d is out of range [%d, %d]\n", if (val_u > param->max_u || val_u < param->min_u) smblib_dbg(chg, PR_MISC, "%s: %d is out of range [%d, %d]\n", param->name, val_u, param->min_u, param->max_u); return -EINVAL; } if (val_u > param->max_u) val_u = param->max_u; if (val_u < param->min_u) val_u = param->min_u; val_raw = (val_u - param->min_u) / param->step_u; } Loading Loading @@ -771,69 +837,46 @@ static int get_sdp_current(struct smb_charger *chg, int *icl_ua) int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) { int rc = 0; bool override; bool hc_mode = false; /* suspend and return if 25mA or less is requested */ if (icl_ua <= USBIN_25MA) return smblib_set_usb_suspend(chg, true); if (icl_ua == INT_MAX) goto override_suspend_config; goto set_mode; /* configure current */ if (chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT if (((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) || (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)) && (chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) { rc = set_sdp_current(chg, icl_ua); if (rc < 0) { smblib_err(chg, "Couldn't set SDP ICL rc=%d\n", rc); goto enable_icl_changed_interrupt; goto out; } } else { set_sdp_current(chg, 100000); rc = smblib_set_charge_param(chg, &chg->param.usb_icl, icl_ua); if (rc < 0) { smblib_err(chg, "Couldn't set HC ICL rc=%d\n", rc); goto enable_icl_changed_interrupt; goto out; } hc_mode = true; } override_suspend_config: /* determine if override needs to be enforced */ override = true; if (icl_ua == INT_MAX) { /* remove override if no voters - hw defaults is desired */ override = false; } else if (chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) { if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB) /* For std cable with type = SDP never override */ override = false; else if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_CDP && icl_ua == 1500000) /* * For std cable with type = CDP override only if * current is not 1500mA */ override = false; } /* enforce override */ set_mode: rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG, USBIN_MODE_CHG_BIT, override ? USBIN_MODE_CHG_BIT : 0); rc = smblib_icl_override(chg, override); if (rc < 0) { smblib_err(chg, "Couldn't set ICL override rc=%d\n", rc); goto enable_icl_changed_interrupt; } USBIN_MODE_CHG_BIT, hc_mode ? USBIN_MODE_CHG_BIT : 0); /* unsuspend after configuring current and override */ rc = smblib_set_usb_suspend(chg, false); if (rc < 0) { smblib_err(chg, "Couldn't resume input rc=%d\n", rc); goto enable_icl_changed_interrupt; goto out; } enable_icl_changed_interrupt: out: return rc; } Loading @@ -844,7 +887,7 @@ int smblib_get_icl_current(struct smb_charger *chg, int *icl_ua) bool override; if ((chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT || chg->micro_usb_mode) || chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) && (chg->usb_psy->desc->type == POWER_SUPPLY_TYPE_USB)) { rc = get_sdp_current(chg, icl_ua); if (rc < 0) { Loading Loading @@ -881,6 +924,9 @@ static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data, { struct smb_charger *chg = data; if (chg->smb_version == PMI632_SUBTYPE) return 0; /* resume input if suspend is invalid */ if (suspend < 0) suspend = 0; Loading Loading @@ -2076,6 +2122,9 @@ int smblib_set_prop_typec_power_role(struct smb_charger *chg, int rc = 0; u8 power_role; if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) return 0; switch (val->intval) { case POWER_SUPPLY_TYPEC_PR_NONE: power_role = TYPEC_DISABLE_CMD_BIT; Loading Loading @@ -2513,7 +2562,7 @@ static void smblib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising) } else { chg->typec_present = 0; smblib_update_usb_type(chg); extcon_set_state_sync(chg->extcon, EXTCON_USB, false); smblib_notify_device_mode(chg, false); smblib_uusb_removal(chg); } } Loading Loading @@ -2601,7 +2650,7 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc); } if (chg->micro_usb_mode) if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) smblib_micro_usb_plugin(chg, vbus_rising); power_supply_changed(chg->usb_psy); Loading Loading @@ -2763,37 +2812,6 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg, rising ? "rising" : "falling"); } static void smblib_notify_extcon_props(struct smb_charger *chg, int id) { union extcon_property_value val; union power_supply_propval prop_val; smblib_get_prop_typec_cc_orientation(chg, &prop_val); val.intval = ((prop_val.intval == 2) ? 1 : 0); extcon_set_property(chg->extcon, id, EXTCON_PROP_USB_TYPEC_POLARITY, val); val.intval = true; extcon_set_property(chg->extcon, id, EXTCON_PROP_USB_SS, val); } static void smblib_notify_device_mode(struct smb_charger *chg, bool enable) { if (enable) smblib_notify_extcon_props(chg, EXTCON_USB); extcon_set_state_sync(chg->extcon, EXTCON_USB, enable); } static void smblib_notify_usb_host(struct smb_charger *chg, bool enable) { if (enable) smblib_notify_extcon_props(chg, EXTCON_USB_HOST); extcon_set_state_sync(chg->extcon, EXTCON_USB_HOST, enable); } static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) { const struct apsd_result *apsd_result; Loading @@ -2806,13 +2824,11 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) switch (apsd_result->bit) { case SDP_CHARGER_BIT: case CDP_CHARGER_BIT: if (chg->micro_usb_mode) extcon_set_state_sync(chg->extcon, EXTCON_USB, true); /* if not DCP then no hvdcp timeout happens. Enable pd here */ vote(chg->pd_disallowed_votable_indirect, APSD_VOTER, false, 0); if (chg->use_extcon) if ((chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) || chg->use_extcon) smblib_notify_device_mode(chg, true); break; case OCP_CHARGER_BIT: Loading Loading @@ -2845,7 +2861,8 @@ irqreturn_t usb_source_change_irq_handler(int irq, void *data) } smblib_dbg(chg, PR_REGISTER, "APSD_STATUS = 0x%02x\n", stat); if (chg->micro_usb_mode && (stat & APSD_DTC_STATUS_DONE_BIT) if ((chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) && (stat & APSD_DTC_STATUS_DONE_BIT) && !chg->uusb_apsd_rerun_done) { /* * Force re-run APSD to handle slow insertion related Loading Loading @@ -3146,12 +3163,12 @@ static void smblib_usb_typec_change(struct smb_charger *chg) power_supply_changed(chg->usb_psy); } irqreturn_t typec_state_change_irq_handler(int irq, void *data) irqreturn_t typec_or_rid_detection_change_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; if (chg->micro_usb_mode) { if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) { cancel_delayed_work_sync(&chg->uusb_otg_work); vote(chg->awake_votable, OTG_DELAY_VOTER, true, 0); smblib_dbg(chg, PR_INTERRUPT, "Scheduling OTG work\n"); Loading @@ -3160,7 +3177,16 @@ irqreturn_t typec_state_change_irq_handler(int irq, void *data) return IRQ_HANDLED; } if (chg->pr_swap_in_progress) { return IRQ_HANDLED; } irqreturn_t typec_state_change_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; if ((chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) || chg->pr_swap_in_progress) { smblib_dbg(chg, PR_INTERRUPT, "Ignoring since pr_swap_in_progress\n"); return IRQ_HANDLED; Loading Loading @@ -3335,10 +3361,20 @@ static void smblib_uusb_otg_work(struct work_struct *work) smblib_err(chg, "Couldn't read TYPE_C_STATUS_3 rc=%d\n", rc); goto out; } otg = !!(stat & U_USB_GROUND_NOVBUS_BIT); if (chg->otg_present != otg) smblib_notify_usb_host(chg, otg); chg->otg_present = otg; if (!otg) chg->boost_current_ua = 0; otg = !!(stat & (U_USB_GROUND_NOVBUS_BIT | U_USB_GROUND_BIT)); extcon_set_state_sync(chg->extcon, EXTCON_USB_HOST, otg); smblib_dbg(chg, PR_REGISTER, "TYPE_C_STATUS_3 = 0x%02x OTG=%d\n", rc = smblib_set_charge_param(chg, &chg->param.freq_switcher, otg ? chg->chg_freq.freq_below_otg_threshold : chg->chg_freq.freq_removal); if (rc < 0) dev_err(chg->dev, "Error in setting freq_boost rc=%d\n", rc); smblib_dbg(chg, PR_REGISTER, "TYPE_C_U_USB_STATUS = 0x%02x OTG=%d\n", stat, otg); power_supply_changed(chg->usb_psy); Loading Loading @@ -3559,13 +3595,6 @@ int smblib_init(struct smb_charger *chg) return rc; } rc = smblib_register_notifier(chg); if (rc < 0) { smblib_err(chg, "Couldn't register notifier rc=%d\n", rc); return rc; } chg->bms_psy = power_supply_get_by_name("bms"); chg->pl.psy = power_supply_get_by_name("parallel"); if (chg->pl.psy) { Loading @@ -3576,6 +3605,12 @@ int smblib_init(struct smb_charger *chg) return rc; } } rc = smblib_register_notifier(chg); if (rc < 0) { smblib_err(chg, "Couldn't register notifier rc=%d\n", rc); return rc; } break; case PARALLEL_SLAVE: break; Loading
drivers/power/supply/qcom/smb5-lib.h +10 −4 Original line number Diff line number Diff line Loading @@ -196,6 +196,11 @@ struct smb_chg_param { u8 *val_raw); }; struct buck_boost_freq { int freq_khz; u8 val; }; struct smb_chg_freq { unsigned int freq_5V; unsigned int freq_6V_8V; Loading Loading @@ -311,7 +316,7 @@ struct smb_charger { bool sw_jeita_enabled; bool is_hdc; bool chg_done; bool micro_usb_mode; int connector_type; bool otg_en; bool suspend_input_on_debug_batt; int otg_attempts; Loading @@ -329,6 +334,7 @@ struct smb_charger { u8 float_cfg; bool use_extcon; bool otg_present; int hw_max_icl_ua; /* workaround flag */ u32 wa_flags; Loading Loading @@ -369,6 +375,8 @@ int smblib_mapping_cc_delta_from_field_value(struct smb_chg_param *param, int val_u, u8 *val_raw); int smblib_set_chg_freq(struct smb_chg_param *param, int val_u, u8 *val_raw); int smblib_set_prop_boost_current(struct smb_charger *chg, const union power_supply_propval *val); int smblib_vbus_regulator_enable(struct regulator_dev *rdev); int smblib_vbus_regulator_disable(struct regulator_dev *rdev); int smblib_vbus_regulator_is_enabled(struct regulator_dev *rdev); Loading @@ -390,6 +398,7 @@ irqreturn_t dc_plugin_irq_handler(int irq, void *data); irqreturn_t high_duty_cycle_irq_handler(int irq, void *data); irqreturn_t switcher_power_ok_irq_handler(int irq, void *data); irqreturn_t wdog_bark_irq_handler(int irq, void *data); irqreturn_t typec_or_rid_detection_change_irq_handler(int irq, void *data); int smblib_get_prop_input_suspend(struct smb_charger *chg, union power_supply_propval *val); Loading Loading @@ -470,8 +479,6 @@ int smblib_set_prop_pd_voltage_max(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_pd_voltage_min(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_boost_current(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_typec_power_role(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_pd_active(struct smb_charger *chg, Loading @@ -484,7 +491,6 @@ void smblib_suspend_on_debug_battery(struct smb_charger *chg); int smblib_rerun_apsd_if_required(struct smb_charger *chg); int smblib_get_prop_fcc_delta(struct smb_charger *chg, union power_supply_propval *val); int smblib_icl_override(struct smb_charger *chg, bool override); int smblib_dp_dm(struct smb_charger *chg, int val); int smblib_disable_hw_jeita(struct smb_charger *chg, bool disable); int smblib_rerun_aicl(struct smb_charger *chg); Loading
drivers/power/supply/qcom/smb5-reg.h +3 −0 Original line number Diff line number Diff line Loading @@ -196,6 +196,7 @@ enum { }; #define USBIN_OPTIONS_1_CFG_REG (USBIN_BASE + 0x62) #define HVDCP_AUTH_ALG_EN_CFG_BIT BIT(6) #define HVDCP_AUTONOMOUS_MODE_EN_CFG_BIT BIT(5) #define BC1P2_SRC_DETECT_BIT BIT(3) Loading Loading @@ -299,6 +300,8 @@ enum { #define TYPEC_U_USB_CFG_REG (TYPEC_BASE + 0x70) #define EN_MICRO_USB_MODE_BIT BIT(0) #define TYPEC_MICRO_USB_MODE_REG (TYPEC_BASE + 0x70) #define MICRO_USB_MODE_ONLY_BIT BIT(0) /******************************** * MISC Peripheral Registers * ********************************/ Loading