From f62bc5be5142ba9fdd9899015050fba79d9be9a7 Mon Sep 17 00:00:00 2001 From: Ashish Chavan Date: Tue, 23 Mar 2021 18:34:46 +0530 Subject: [PATCH] power: supply: smblite: Add support for concurrency mode Add an interface to enable/disable boost concurrency. When enabled the charger in buck-mode can act as a boost over a separate path. The feature is supported only on PM5100 and can be enabled using "qcom,concurrency-mode-supported" flag. Change-Id: I3ab78af2123add2c4d5cba96500e1576b3c7deb3 Signed-off-by: Ashish Chavan --- drivers/power/supply/qcom/qpnp-smblite.c | 58 +++++- drivers/power/supply/qcom/smblite-lib.c | 252 +++++++++++++++++++---- drivers/power/supply/qcom/smblite-lib.h | 7 + drivers/power/supply/qcom/smblite-reg.h | 6 + 4 files changed, 280 insertions(+), 43 deletions(-) diff --git a/drivers/power/supply/qcom/qpnp-smblite.c b/drivers/power/supply/qcom/qpnp-smblite.c index 1f705ddfdb0b..649a2876d408 100644 --- a/drivers/power/supply/qcom/qpnp-smblite.c +++ b/drivers/power/supply/qcom/qpnp-smblite.c @@ -183,11 +183,12 @@ ATTRIBUTE_GROUPS(smblite); static int smblite_chg_config_init(struct smblite *chip) { struct smb_charger *chg = &chip->chg; - int subtype = (u8)of_device_get_match_data(chg->dev); u8 val; int rc = 0; - switch (subtype) { + chg->subtype = (u8)of_device_get_match_data(chg->dev); + + switch (chg->subtype) { case PM2250: chg->wa_flags |= WEAK_ADAPTER_WA; chg->base = smb_base[PM2250]; @@ -209,7 +210,7 @@ static int smblite_chg_config_init(struct smblite *chip) chg->name = "PM5100_charger"; break; default: - pr_err("Unsupported PMIC subtype=%d\n", subtype); + pr_err("Unsupported PMIC subtype=%d\n", chg->subtype); return -EINVAL; } @@ -302,6 +303,9 @@ static int smblite_parse_dt_misc(struct smblite *chip, struct device_node *node) if (chg->chg_param.fcc_step_size_ua <= 0) chg->chg_param.fcc_step_size_ua = DEFAULT_FCC_STEP_SIZE_UA; + chg->concurrent_mode_supported = of_property_read_bool(node, + "qcom,concurrency-mode-supported"); + return 0; } @@ -771,6 +775,41 @@ static int smblite_init_batt_psy(struct smblite *chip) return rc; } +/******************* + * QCOM SMB Class * + *******************/ +static ssize_t boost_concurrent_mode_store(struct class *c, struct class_attribute *attr, + const char *buf, size_t count) +{ + struct smb_charger *chg = container_of(c, struct smb_charger, qcom_class); + int rc; + bool val; + + if (kstrtobool(buf, &val)) + return -EINVAL; + + rc = smblite_lib_set_concurrent_config(chg, val); + if (rc < 0) + return rc; + + return count; +} + +static ssize_t boost_concurrent_mode_show(struct class *c, struct class_attribute *attr, + char *buf) +{ + struct smb_charger *chg = container_of(c, struct smb_charger, qcom_class); + + return scnprintf(buf, PAGE_SIZE, "%d\n", chg->concurrent_mode_status); +} +static CLASS_ATTR_RW(boost_concurrent_mode); + +static struct attribute *qcom_class_attrs[] = { + &class_attr_boost_concurrent_mode.attr, + NULL, +}; +ATTRIBUTE_GROUPS(qcom_class); + /*************************** * HARDWARE INITIALIZATION * ***************************/ @@ -1964,6 +2003,18 @@ static int smblite_probe(struct platform_device *pdev) goto disable_irq; } + /* Register QCOM SMB class */ + if (is_concurrent_mode_supported(chg)) { + chg->qcom_class.name = "qcom-smb"; + chg->qcom_class.owner = THIS_MODULE; + chg->qcom_class.class_groups = qcom_class_groups; + rc = class_register(&chg->qcom_class); + if (rc < 0) { + pr_err("Failed to create qcom_class rc=%d\n", rc); + goto disable_irq; + } + } + device_init_wakeup(chg->dev, true); rc = smblite_lib_get_prop_usb_present(chg, &pval); @@ -1991,6 +2042,7 @@ static int smblite_remove(struct platform_device *pdev) struct smb_charger *chg = &chip->chg; smblite_disable_interrupts(chg); + class_destroy(&chg->qcom_class); smblite_lib_deinit(chg); sysfs_remove_groups(&chg->dev->kobj, smblite_groups); platform_set_drvdata(pdev, NULL); diff --git a/drivers/power/supply/qcom/smblite-lib.c b/drivers/power/supply/qcom/smblite-lib.c index 67942397792c..7e9b46d32dc9 100644 --- a/drivers/power/supply/qcom/smblite-lib.c +++ b/drivers/power/supply/qcom/smblite-lib.c @@ -523,6 +523,40 @@ static int smblite_lib_register_notifier(struct smb_charger *chg) return 0; } +bool is_concurrent_mode_supported(struct smb_charger *chg) +{ + return (chg->concurrent_mode_supported && chg->subtype == PM5100); +} + +static int smblite_lib_concurrent_mode_config(struct smb_charger *chg, bool enable) +{ + int rc; + + if (!is_concurrent_mode_supported(chg)) + return 0; + + rc = smblite_lib_write(chg, CONCURRENT_MODE_CFG_REG(chg->base), + (enable ? CONCURRENT_MODE_EN_BIT : 0)); + if (rc < 0) + smblite_lib_err(chg, "Failed to write CONCURRENT_MODE_CFG_REG rc=%d\n", + rc); + + if (!enable) { + /* Remove usb_icl_vote when concurrency mode is disabled */ + rc = vote(chg->usb_icl_votable, CONCURRENT_MODE_VOTER, false, + 0); + if (rc < 0) + smblite_lib_err(chg, "Failed to vote on ICL rc=%d\n", rc); + + /* Remove chg_disable_vote when concurrency mode is disabled */ + rc = vote(chg->chg_disable_votable, CONCURRENT_MODE_VOTER, false, 0); + if (rc < 0) + smblite_lib_err(chg, "Failed to vote on ICL rc=%d\n", rc); + } + + return rc; +} + static void smblite_lib_uusb_removal(struct smb_charger *chg) { int rc; @@ -563,6 +597,8 @@ static void smblite_lib_uusb_removal(struct smb_charger *chg) chg->uusb_apsd_rerun_done = false; chg->hvdcp3_detected = false; + /* Disable concurrent mode on USB removal. */ + smblite_lib_concurrent_mode_config(chg, false); } void smblite_lib_suspend_on_debug_battery(struct smb_charger *chg) @@ -1299,7 +1335,52 @@ static int smblite_lib_dp_pulse(struct smb_charger *chg) return rc; } -int smblite_lib_force_vbus_voltage(struct smb_charger *chg, u8 val) +#define HVDCP3_QUALIFICATION_UV (PM5100_MAX_HVDCP3_PULSES * \ + (HVDCP3_STEP_SIZE_UV / 2)) +static int smblite_lib_hvdcp3_force_max_vbus(struct smb_charger *chg) +{ + union power_supply_propval pval = {0, }; + int cnt = 0, rc, prev_vbus; + bool qc3_detected = false; + + rc = smblite_lib_get_prop_usb_voltage_now(chg, &pval); + if (rc < 0) { + smblite_lib_err(chg, "Couldn't read voltage_now rc=%d\n", + rc); + return rc; + } + + prev_vbus = pval.intval; + + /* + * Statically increase voltage till 6V. + * ( i.e : 1V / 200mV = 5 pulses ). + */ + while (cnt++ < PM5100_MAX_HVDCP3_PULSES) { + smblite_lib_dp_pulse(chg); + /* wait for 100ms for vbus to settle. */ + msleep(100); + } + + rc = smblite_lib_get_prop_usb_voltage_now(chg, &pval); + if (rc < 0) { + smblite_lib_err(chg, "Couldn't read voltage_now rc=%d\n", + rc); + return rc; + } + + /* Check if voltage incremented. (i.e if QC3 ) */ + if (pval.intval >= (prev_vbus + HVDCP3_QUALIFICATION_UV)) + qc3_detected = true; + + smblite_lib_dbg(chg, PR_MISC, "HVDCP3 : detected=%s, prev_vbus=%d, vbus_now=%d\n", + (qc3_detected ? "True" : "False"), prev_vbus, + pval.intval); + + return qc3_detected; +} + +static int smblite_lib_force_vbus_voltage(struct smb_charger *chg, u8 val) { int rc; @@ -1356,6 +1437,111 @@ int smblite_lib_run_aicl(struct smb_charger *chg, int type) return 0; } +#define CONCURRENCY_REDUCED_ICL_UA 300000 +int smblite_lib_set_concurrent_config(struct smb_charger *chg, bool enable) +{ + int rc = 0, icl_ua = 0, settled_icl_ua = 0, usb_present = 0; + union power_supply_propval pval = {0, }; + + if (!is_concurrent_mode_supported(chg)) { + smblite_lib_dbg(chg, PR_MISC, "Concurrent Mode supported disabled\n"); + return 0; + } + + /* Exit if there is no change in state */ + if (chg->concurrent_mode_status == enable) + goto out; + + rc = smblite_lib_get_prop_usb_present(chg, &pval); + if (rc < 0) { + smblite_lib_dbg(chg, PR_MISC, + "Couldn't get USB preset status rc=%d\n", rc); + goto failure; + } + usb_present = pval.intval; + + if (enable) { + /* Check if USB is connected */ + if (!usb_present) { + smblite_lib_dbg(chg, PR_MISC, + "Failed to enable concurrent mode USB disconnected\n", rc); + goto failure; + } + + /* Get AICL Result */ + rc = smblite_lib_get_prop_input_current_settled(chg, &settled_icl_ua); + if (rc) { + smblite_lib_err(chg, "Failed read AICL Result rc=%d\n", rc); + goto failure; + } + + /* Return if AICL result is less than 300mA. */ + if (settled_icl_ua <= CONCURRENCY_REDUCED_ICL_UA) { + smblite_lib_dbg(chg, PR_MISC, + "AICL Result too less to enable concurreny mode\n"); + goto failure; + } + + icl_ua = settled_icl_ua - CONCURRENCY_REDUCED_ICL_UA; + + rc = vote(chg->usb_icl_votable, CONCURRENT_MODE_VOTER, true, + icl_ua); + if (rc < 0) { + smblite_lib_err(chg, "Failed to vote on ICL rc=%d\n", rc); + goto failure; + } + + if (chg->hvdcp3_detected) { + /* Force Vbus to 5V. */ + rc = smblite_lib_force_vbus_voltage(chg, FORCE_5V_BIT); + if (rc < 0) + smblite_lib_err(chg, "Failed to force vbus to 5V rc=%d\n", + rc); + } + + /* Enable charger if already disabled */ + rc = vote(chg->chg_disable_votable, CONCURRENT_MODE_VOTER, false, 0); + if (rc < 0) { + smblite_lib_err(chg, "Failed to Enable charger rc=%d\n", + rc); + goto failure; + } + + /* Enable concurrent mode */ + rc = smblite_lib_concurrent_mode_config(chg, true); + if (rc < 0) + goto failure; + + chg->concurrent_mode_status = true; + smblite_lib_dbg(chg, PR_MISC, "Concurrent Mode enabled successfully: settled_icl_ua=%duA, icl_ua=%duA, is_hvdcp3=%d\n", + settled_icl_ua, icl_ua, + chg->hvdcp3_detected); + goto out; + } else { + /* Disable concurrent mode */ + rc = smblite_lib_concurrent_mode_config(chg, false); + if (rc < 0) + goto failure; + + /* Restore vbus to MAX(6V) if QC3P5 is connected */ + if (chg->hvdcp3_detected && usb_present) + smblite_lib_hvdcp3_force_max_vbus(chg); + + chg->concurrent_mode_status = false; + smblite_lib_dbg(chg, PR_MISC, "Concurrent Mode disabled successfully: is_hvdcp3=%d\n", + chg->hvdcp3_detected); + goto out; + } + +failure: + rc = -EINVAL; + smblite_lib_dbg(chg, PR_MISC, "Failed to %s concurrent mode\n", + (enable ? "Enable" : "Disable")); + +out: + return rc; +} + /******************* * USB PSY GETTERS * *******************/ @@ -2397,12 +2583,30 @@ static void smblite_lib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising) { int rc = 0; - + u8 stat; if (vbus_rising) { rc = typec_partner_register(chg); if (rc < 0) smblite_lib_err(chg, "Couldn't register partner rc =%d\n", rc); + + /* + * For PM5100 check if concurrent mode support is enabled and + * charging is paused in hardware due to boost being enabled, + * force charging to be disabled in SW. + */ + if (is_concurrent_mode_supported(chg)) { + rc = smblite_lib_read(chg, CHGR_CHG_EN_STATUS_REG(chg->base), &stat); + if (rc < 0) + smblite_lib_err(chg, "Couldn't read CHGR_EN_STATUS_REG rc=%d\n", + rc); + + vote(chg->chg_disable_votable, CONCURRENT_MODE_VOTER, + (stat & CHARGING_DISABLED_FROM_BOOST_BIT), 0); + + smblite_lib_dbg(chg, PR_MISC, + "charger_en_status=%x, Charging disable by boost\n", stat); + } } else { smblite_lib_notify_device_mode(chg, false); smblite_lib_uusb_removal(chg); @@ -2695,54 +2899,22 @@ static void smblite_lib_handle_apsd_done(struct smb_charger *chg, bool rising) apsd_result->name); } -#define HVDCP3_QUALIFICATION_UV (PM5100_MAX_HVDCP3_PULSES * \ - (HVDCP3_STEP_SIZE_UV / 2)) static void smblite_lib_handle_hvdcp_check_timeout(struct smb_charger *chg, bool rising, bool qc_charger) { - union power_supply_propval pval = {0, }; - int rc = 0, cnt = 0, prev_vbus; + int rc = 0; if (rising) { - if (qc_charger) { - rc = smblite_lib_get_prop_usb_voltage_now(chg, &pval); - if (rc < 0) { - smblite_lib_err(chg, "Couldn't read voltage_now rc=%d\n", - rc); - goto failure; - } - - prev_vbus = pval.intval; - - /* - * Statically increase voltage till 6V. - * ( i.e : 1V / 200mV = 5 pulses ). - */ - while (cnt++ < PM5100_MAX_HVDCP3_PULSES) { - smblite_lib_dp_pulse(chg); - /* wait for 100ms for vbus to settle. */ - msleep(100); - } - - rc = smblite_lib_get_prop_usb_voltage_now(chg, &pval); - if (rc < 0) { - smblite_lib_err(chg, "Couldn't read voltage_now rc=%d\n", - rc); - goto failure; - } - - /* Check if voltage incremented. (i.e if QC3 )*/ - if (pval.intval >= (prev_vbus + HVDCP3_QUALIFICATION_UV)) + /* Increase vbus to MAX(6V), if incremented HVDCP_3 is detected */ + rc = smblite_lib_hvdcp3_force_max_vbus(chg); + if (rc < 0) + smblite_lib_err(chg, "HVDCP3 detection failure\n"); + if (rc > 0) chg->hvdcp3_detected = true; - - smblite_lib_dbg(chg, PR_MISC, "HVDCP3 : detected=%s, prev_vbus=%d, vbus_now=%d\n", - (chg->hvdcp3_detected ? "True" : "False"), - prev_vbus, pval.intval); } } -failure: smblite_lib_dbg(chg, PR_INTERRUPT, "IRQ: %s %s\n", __func__, rising ? "rising" : "falling"); } diff --git a/drivers/power/supply/qcom/smblite-lib.h b/drivers/power/supply/qcom/smblite-lib.h index 023301631f0d..d5bd7ae2c3c7 100644 --- a/drivers/power/supply/qcom/smblite-lib.h +++ b/drivers/power/supply/qcom/smblite-lib.h @@ -51,6 +51,7 @@ enum print_reason { #define ICL_CHANGE_VOTER "ICL_CHANGE_VOTER" #define TYPEC_SWAP_VOTER "TYPEC_SWAP_VOTER" #define FLASH_ACTIVE_VOTER "FLASH_ACTIVE_VOTER" +#define CONCURRENT_MODE_VOTER "CONCURRENT_MODE_VOTER" #define BOOST_BACK_STORM_COUNT 3 #define WEAK_CHG_STORM_COUNT 8 @@ -264,8 +265,10 @@ struct smb_charger { struct iio_channel *iio_chans; struct iio_channel **iio_chan_list_qg; struct iio_channel **iio_chan_list_smb_parallel; + struct class qcom_class; int *debug_mask; enum smb_mode mode; + u8 subtype; int weak_chg_icl_ua; /* locks */ @@ -359,6 +362,8 @@ struct smb_charger { bool uusb_apsd_rerun_done; bool dpdm_enabled; bool hvdcp3_detected; + bool concurrent_mode_supported; + bool concurrent_mode_status; /* workaround flag */ u32 wa_flags; @@ -503,5 +508,7 @@ int smblite_iio_get_prop(struct smb_charger *chg, int channel, int *val); int smblite_iio_set_prop(struct smb_charger *chg, int channel, int val); int smblite_lib_get_fcc(struct smb_chg_param *param, u8 val_raw); int smblite_lib_set_fcc(struct smb_chg_param *param, int val_u, u8 *val_raw); +int smblite_lib_set_concurrent_config(struct smb_charger *chg, bool enable); +bool is_concurrent_mode_supported(struct smb_charger *chg); #endif /* __SMBLITE_LIB_H */ diff --git a/drivers/power/supply/qcom/smblite-reg.h b/drivers/power/supply/qcom/smblite-reg.h index 14f5c80e5751..184f32a488d8 100644 --- a/drivers/power/supply/qcom/smblite-reg.h +++ b/drivers/power/supply/qcom/smblite-reg.h @@ -30,6 +30,9 @@ enum { DISABLE_CHARGE, }; +#define CHGR_CHG_EN_STATUS_REG(base) (base.chg_base + 0x07) +#define CHARGING_DISABLED_FROM_BOOST_BIT BIT(6) + #define CHARGER_VBAT_STATUS_REG(base) (base.chg_base + 0x08) #define BAT_OV_BIT BIT(7) @@ -103,6 +106,9 @@ enum { #define DCDC_LDO_CFG_REG(base) (base.dcdc_base + 0x70) #define LDO_MODE_BIT BIT(0) +#define CONCURRENT_MODE_CFG_REG(base) (base.dcdc_base + 0x50) +#define CONCURRENT_MODE_EN_BIT BIT(0) + /******************************** * BATIF Peripheral Registers * ********************************/