diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c index 8467d0fbddf4..7b1e249cd17a 100644 --- a/drivers/iio/adc/qcom-vadc-common.c +++ b/drivers/iio/adc/qcom-vadc-common.c @@ -555,7 +555,85 @@ struct lut_table { u32 tablesize; }; +#ifdef CONFIG_BAT_NTC_10K +static const struct vadc_map_pt adcmap_batt_therm_30k_NTC10K[] = { + {1618, -400}, + {1593, -380}, + {1566, -360}, + {1538, -340}, + {1508, -320}, + {1477, -300}, + {1444, -280}, + {1410, -260}, + {1374, -240}, + {1338, -220}, + {1300, -200}, + {1261, -180}, + {1221, -160}, + {1181, -140}, + {1140, -120}, + {1099, -100}, + {1057, -80}, + {1016, -60}, + {974, -40}, + {934, -20}, + {893, 0}, + {853, 20}, + {814, 40}, + {776, 60}, + {738, 80}, + {702, 100}, + {667, 120}, + {633, 140}, + {600, 160}, + {569, 180}, + {539, 200}, + {510, 220}, + {482, 240}, + {456, 260}, + {431, 280}, + {407, 300}, + {384, 320}, + {363, 340}, + {342, 360}, + {323, 380}, + {305, 400}, + {288, 420}, + {272, 440}, + {256, 460}, + {242, 480}, + {228, 500}, + {215, 520}, + {203, 540}, + {192, 560}, + {181, 580}, + {171, 600}, + {162, 620}, + {153, 640}, + {145, 660}, + {137, 680}, + {130, 700}, + {123, 720}, + {116, 740}, + {110, 760}, + {104, 780}, + {99, 800}, + {94, 820}, + {89, 840}, + {84, 860}, + {80, 880}, + {76, 900}, + {72, 920}, + {69, 940}, + {65, 960}, + {62, 980} +}; +#endif + static const struct lut_table lut_table_30[] = { +#ifdef CONFIG_BAT_NTC_10K + {adcmap_batt_therm_30k_NTC10K, ARRAY_SIZE(adcmap_batt_therm_30k_NTC10K)}, +#endif {adcmap_batt_therm_30k, ARRAY_SIZE(adcmap_batt_therm_30k)}, {adcmap_batt_therm_30k_6125, ARRAY_SIZE(adcmap_batt_therm_30k_6125)}, }; diff --git a/drivers/of/of_batterydata.c b/drivers/of/of_batterydata.c index 0a6ad38653d7..f0477ce283df 100644 --- a/drivers/of/of_batterydata.c +++ b/drivers/of/of_batterydata.c @@ -308,6 +308,85 @@ static int64_t of_batterydata_convert_battery_id_kohm(int batt_id_uv, return resistor_value_kohm; } +static const char *get_battery_serialnumber(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + const char *battsn_buf; + int retval; + + if (np) + retval = of_property_read_string(np, "mmi,battid", + &battsn_buf); + else + return NULL; + + if ((retval == -EINVAL) || !battsn_buf) { + pr_err("Battsn unused\n"); + of_node_put(np); + return NULL; + + } else + pr_err("Battsn = %s\n", battsn_buf); + + of_node_put(np); + return battsn_buf; +} + +static struct device_node *get_profile_by_serialnumber( + const struct device_node *np) +{ + struct device_node *node, *df_node, *sn_node; + const char *sn_buf, *df_sn, *dev_sn; + int rc; + + if (!np) + return NULL; + + dev_sn = NULL; + df_sn = NULL; + sn_buf = NULL; + df_node = NULL; + sn_node = NULL; + + dev_sn = get_battery_serialnumber(); + + rc = of_property_read_string(np, "df-serialnum", + &df_sn); + if (rc) + pr_warn("No Default Serial Number defined\n"); + else if (df_sn) + pr_info("Default Serial Number %s\n", df_sn); + + for_each_child_of_node(np, node) { + rc = of_property_read_string(node, "serialnum", + &sn_buf); + if (!rc && sn_buf) { + if (dev_sn) + if (strnstr(dev_sn, sn_buf, 32)) + sn_node = node; + if (df_sn) + if (strnstr(df_sn, sn_buf, 32)) + df_node = node; + } + } + + if (sn_node) { + node = sn_node; + df_node = NULL; + pr_info("Battery Match Found using %s\n", sn_node->name); + } else if (df_node) { + node = df_node; + sn_node = NULL; + pr_info("Battery Match Found using default %s\n", + df_node->name); + } else { + pr_warn("No Battery Match Found!\n"); + return NULL; + } + + return node; +} + struct device_node *of_batterydata_get_best_profile( const struct device_node *batterydata_container_node, int batt_id_kohm, const char *batt_type) @@ -332,6 +411,10 @@ struct device_node *of_batterydata_get_best_profile( } } + best_node = get_profile_by_serialnumber(batterydata_container_node); + if (best_node) + return best_node; + /* * Find the battery data with a battery id resistor closest to this one */ diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index bbccf3f836c0..3a1367d10f3d 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -464,6 +464,13 @@ config CHARGER_GPIO This driver can be build as a module. If so, the module will be called gpio-charger. +config BAT_NTC_10K + bool "for ntc 10k" + default n + help + Say Y to enable + for battery ntc 10k + config CHARGER_MANAGER bool "Battery charger manager for multiple chargers" depends on REGULATOR diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index 633e006b3984..1a3ce81e500e 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -153,6 +153,8 @@ static void power_supply_deferred_register_work(struct work_struct *work) } psy_register_cooler(psy->dev.parent, psy); + atomic_notifier_call_chain(&power_supply_notifier, + PSY_EVENT_PROP_ADDED, psy); power_supply_changed(psy); if (psy->dev.parent) @@ -463,7 +465,10 @@ struct power_supply *power_supply_get_by_name(const char *name) if (dev) { psy = dev_get_drvdata(dev); - atomic_inc(&psy->use_cnt); + if (atomic_read(&psy->use_cnt) >= 1) + atomic_inc(&psy->use_cnt); + else + psy = NULL; } return psy; @@ -522,7 +527,10 @@ struct power_supply *power_supply_get_by_phandle(struct device_node *np, if (dev) { psy = dev_get_drvdata(dev); - atomic_inc(&psy->use_cnt); + if (atomic_read(&psy->use_cnt) >= 1) + atomic_inc(&psy->use_cnt); + else + psy = NULL; } return psy; @@ -629,7 +637,8 @@ int power_supply_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - if (atomic_read(&psy->use_cnt) <= 0) { + if (!psy || atomic_read(&psy->use_cnt) <= 0 || + !psy->desc || !psy->desc->get_property) { if (!psy->initialized) return -EAGAIN; return -ENODEV; @@ -643,7 +652,8 @@ int power_supply_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { - if (atomic_read(&psy->use_cnt) <= 0 || !psy->desc->set_property) + if (!psy || atomic_read(&psy->use_cnt) <= 0 || + !psy->desc || !psy->desc->set_property) return -ENODEV; return psy->desc->set_property(psy, psp, val); @@ -653,8 +663,8 @@ EXPORT_SYMBOL_GPL(power_supply_set_property); int power_supply_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { - if (atomic_read(&psy->use_cnt) <= 0 || - !psy->desc->property_is_writeable) + if (!psy || atomic_read(&psy->use_cnt) <= 0 || + !psy->desc || !psy->desc->property_is_writeable) return -ENODEV; return psy->desc->property_is_writeable(psy, psp); @@ -663,8 +673,8 @@ EXPORT_SYMBOL_GPL(power_supply_property_is_writeable); void power_supply_external_power_changed(struct power_supply *psy) { - if (atomic_read(&psy->use_cnt) <= 0 || - !psy->desc->external_power_changed) + if (!psy || atomic_read(&psy->use_cnt) <= 0 || + !psy->desc || !psy->desc->external_power_changed) return; psy->desc->external_power_changed(psy); @@ -679,9 +689,15 @@ EXPORT_SYMBOL_GPL(power_supply_powers); static void power_supply_dev_release(struct device *dev) { - struct power_supply *psy = to_power_supply(dev); - dev_dbg(dev, "%s\n", __func__); + struct power_supply *psy = container_of(dev, struct power_supply, dev); + void *drvr_data = NULL; + + if (psy->free_pdd_on_release) + drvr_data = psy->drv_data; + pr_warn("device: '%s': %s\n", dev_name(dev), __func__); kfree(psy); + if (drvr_data) + kfree(drvr_data); } int power_supply_reg_notifier(struct notifier_block *nb) @@ -895,6 +911,8 @@ __power_supply_register(struct device *parent, cfg->fwnode ? to_of_node(cfg->fwnode) : cfg->of_node; psy->supplied_to = cfg->supplied_to; psy->num_supplicants = cfg->num_supplicants; + if (cfg->drv_data) + psy->free_pdd_on_release = cfg->free_drv_data; } rc = dev_set_name(dev, "%s", desc->name); @@ -1093,6 +1111,8 @@ void power_supply_unregister(struct power_supply *psy) { WARN_ON(atomic_dec_return(&psy->use_cnt)); psy->removing = true; + atomic_notifier_call_chain(&power_supply_notifier, + PSY_EVENT_PROP_REMOVED, psy); cancel_work_sync(&psy->changed_work); cancel_delayed_work_sync(&psy->deferred_register_work); sysfs_remove_link(&psy->dev.kobj, "powers"); diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index a8ad7464e251..689189fb067d 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -97,6 +97,122 @@ static const char * const power_supply_usbc_pr_text[] = { static const char * const power_supply_typec_src_rp_text[] = { "Rp-Default", "Rp-1.5A", "Rp-3A" }; +static char *charge_rate[] = { + "None", "Normal", "Weak", "Turbo", "Hyper" +}; + +static ssize_t power_supply_check_property(struct device *dev, + struct device_attribute *attr, + union power_supply_propval value) { + ssize_t ret = 0; + struct power_supply *psy = dev_get_drvdata(dev); + const ptrdiff_t off = attr - power_supply_attrs; + + if (!psy ||!psy->desc) { + dev_err(dev, "no dev\n"); + return -ENODEV; + } + + if (off == POWER_SUPPLY_PROP_STATUS + && (value.intval < 0 || + value.intval > ARRAY_SIZE(power_supply_status_text))) { + dev_err(dev, "power_supply_status_text, invaid intval %d\n", + value.intval); + goto check_failed; + } else if (off == POWER_SUPPLY_PROP_CHARGE_TYPE + && (value.intval < 0 || + value.intval > ARRAY_SIZE(power_supply_charge_type_text))) { + dev_err(dev, "power_supply_charge_type_text, invaid intval %d\n", + value.intval); + goto check_failed; + } else if (off == POWER_SUPPLY_PROP_CHARGE_RATE + && (value.intval < 0 || + value.intval > ARRAY_SIZE(charge_rate))) { + dev_err(dev, "charge_rate, invaid intval %d\n", + value.intval); + goto check_failed; + } else if (off == POWER_SUPPLY_PROP_HEALTH + && (value.intval < 0 || + value.intval > ARRAY_SIZE(power_supply_health_text))) { + dev_err(dev, "power_supply_health_text, invaid intval %d\n", + value.intval); + goto check_failed; + } else if (off == POWER_SUPPLY_PROP_TECHNOLOGY + && (value.intval < 0 || + value.intval > ARRAY_SIZE(power_supply_technology_text))) { + dev_err(dev, "power_supply_technology_text, invaid intval %d\n", + value.intval); + goto check_failed; + } else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL + && (value.intval < 0 || + value.intval > ARRAY_SIZE(power_supply_capacity_level_text))) { + dev_err(dev, "power_supply_capacity_level_text, invaid intval %d\n", + value.intval); + goto check_failed; + } else if ((off == POWER_SUPPLY_PROP_TYPE || + off == POWER_SUPPLY_PROP_REAL_TYPE) + && (value.intval < 0 || + value.intval > ARRAY_SIZE(power_supply_type_text))) { + dev_err(dev, "power_supply_type_text, invaid intval %d\n", + value.intval); + goto check_failed; + } else if (off == POWER_SUPPLY_PROP_SCOPE + && (value.intval < 0 || + value.intval > ARRAY_SIZE(power_supply_scope_text))) { + dev_err(dev, "power_supply_scope_text, invaid intval %d\n", + value.intval); + goto check_failed; + } else if (off == POWER_SUPPLY_PROP_TYPEC_MODE + && (value.intval < 0 || + value.intval > ARRAY_SIZE(power_supply_usbc_text))) { + dev_err(dev, "power_supply_usbc_text, invaid intval %d\n", + value.intval); + goto check_failed; + } else if (off == POWER_SUPPLY_PROP_TYPEC_POWER_ROLE + && (value.intval < 0 || + value.intval > ARRAY_SIZE(power_supply_usbc_pr_text))) { + dev_err(dev, "power_supply_usbc_pr_text, invaid intval %d\n", + value.intval); + goto check_failed; + } else if (off == POWER_SUPPLY_PROP_TYPEC_SRC_RP + && (value.intval < 0 || + value.intval > ARRAY_SIZE(power_supply_typec_src_rp_text))) { + dev_err(dev, "power_supply_typec_src_rp_text, invaid intval %d\n", + value.intval); + goto check_failed; + } else if (off == POWER_SUPPLY_PROP_DIE_HEALTH + && (value.intval < 0 || + value.intval > ARRAY_SIZE(power_supply_health_text))) { + dev_err(dev, "power_supply_die_health_text, invaid intval %d\n", + value.intval); + goto check_failed; + } else if (off == POWER_SUPPLY_PROP_CONNECTOR_HEALTH + && (value.intval < 0 || + value.intval > ARRAY_SIZE(power_supply_health_text))) { + dev_err(dev, "power_supply_connector_health_text, invaid intval %d\n", + value.intval); + goto check_failed; + } else if (off == POWER_SUPPLY_PROP_SKIN_HEALTH + && (value.intval < 0 || + value.intval > ARRAY_SIZE(power_supply_health_text))) { + dev_err(dev, "power_supply_skin_health_text, invaid intval %d\n", + value.intval); + goto check_failed; + } else if (off >= POWER_SUPPLY_PROP_MODEL_NAME + && !value.strval) { + dev_err(dev, "power_supply_mode_name is null\n"); + goto check_failed; + } + + return ret; + +check_failed: + + if (psy->desc->name) + dev_err(dev, "psy : %s\n", psy->desc->name); + ret = -ENODATA; + return ret; +} static ssize_t power_supply_show_usb_type(struct device *dev, enum power_supply_usb_type *usb_types, @@ -158,6 +274,9 @@ static ssize_t power_supply_show_property(struct device *dev, } } + if (power_supply_check_property(dev, attr, value) < 0) + return ret; + switch (psp) { case POWER_SUPPLY_PROP_STATUS: ret = sprintf(buf, "%s\n", @@ -167,6 +286,10 @@ static ssize_t power_supply_show_property(struct device *dev, ret = sprintf(buf, "%s\n", power_supply_charge_type_text[value.intval]); break; + case POWER_SUPPLY_PROP_CHARGE_RATE: + ret = snprintf(buf, strlen(charge_rate[value.intval]) + 2, + "%s\n", charge_rate[value.intval]); + break; case POWER_SUPPLY_PROP_HEALTH: ret = sprintf(buf, "%s\n", power_supply_health_text[value.intval]); @@ -309,6 +432,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(charge_empty), POWER_SUPPLY_ATTR(charge_now), POWER_SUPPLY_ATTR(charge_avg), + POWER_SUPPLY_ATTR(charge_rate), POWER_SUPPLY_ATTR(charge_counter), POWER_SUPPLY_ATTR(constant_charge_current), POWER_SUPPLY_ATTR(constant_charge_current_max), @@ -374,6 +498,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(temp_cold), POWER_SUPPLY_ATTR(temp_hot), POWER_SUPPLY_ATTR(system_temp_level), + POWER_SUPPLY_ATTR(num_system_temp_levels), POWER_SUPPLY_ATTR(resistance), POWER_SUPPLY_ATTR(resistance_capacitive), POWER_SUPPLY_ATTR(resistance_id), @@ -464,6 +589,7 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(comp_clamp_level), POWER_SUPPLY_ATTR(adapter_cc_mode), POWER_SUPPLY_ATTR(skin_health), + POWER_SUPPLY_ATTR(vbus_conflict), POWER_SUPPLY_ATTR(aicl_done), POWER_SUPPLY_ATTR(voltage_step), POWER_SUPPLY_ATTR(apsd_rerun), @@ -484,6 +610,21 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(cc_toggle_enable), POWER_SUPPLY_ATTR(fg_type), POWER_SUPPLY_ATTR(charger_status), + POWER_SUPPLY_ATTR(internal_send), + POWER_SUPPLY_ATTR(internal_receive), + POWER_SUPPLY_ATTR(external), + POWER_SUPPLY_ATTR(current_flow), + POWER_SUPPLY_ATTR(max_input_current), + POWER_SUPPLY_ATTR(max_output_current), + POWER_SUPPLY_ATTR(external_present), + POWER_SUPPLY_ATTR(power_required), + POWER_SUPPLY_ATTR(power_available), + POWER_SUPPLY_ATTR(power_source), + POWER_SUPPLY_ATTR(max_output_voltage), + POWER_SUPPLY_ATTR(output_voltage), + POWER_SUPPLY_ATTR(max_input_voltage), + POWER_SUPPLY_ATTR(input_voltage), + POWER_SUPPLY_ATTR(age), /* Local extensions of type int64_t */ POWER_SUPPLY_ATTR(charge_counter_ext), /* Properties of type `const char *' */ diff --git a/drivers/power/supply/qcom/Makefile b/drivers/power/supply/qcom/Makefile index b9cc5e6b00f2..0f112f4ef954 100644 --- a/drivers/power/supply/qcom/Makefile +++ b/drivers/power/supply/qcom/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_SMB1360_CHARGER_FG) += smb1360-charger-fg.o -obj-$(CONFIG_QPNP_SMB5) += step-chg-jeita.o battery.o qpnp-smb5.o smb5-lib.o pmic-voter.o storm-watch.o schgm-flash.o +obj-$(CONFIG_QPNP_SMB5) += step-chg-jeita.o battery.o qpnp-smb5.o smb5-lib.o qpnp-smbcharger-mmi.o pmic-voter.o storm-watch.o schgm-flash.o obj-$(CONFIG_SMB1390_CHARGE_PUMP_PSY) += smb1390-charger-psy.o pmic-voter.o obj-$(CONFIG_SMB1355_SLAVE_CHARGER) += smb1355-charger.o pmic-voter.o obj-$(CONFIG_SMB1351_USB_CHARGER) += smb1351-charger.o pmic-voter.o battery.o diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c index ec45b7d1c502..46452d08036b 100644 --- a/drivers/power/supply/qcom/battery.c +++ b/drivers/power/supply/qcom/battery.c @@ -1316,7 +1316,7 @@ static int usb_icl_vote_callback(struct votable *votable, void *data, } /* rerun AICL if new ICL is above settled ICL */ - if (icl_ua > pval.intval) + if (icl_ua != INT_MAX && icl_ua > pval.intval) rerun_aicl = true; if (rerun_aicl && (chip->wa_flags & AICL_RERUN_WA_BIT)) { diff --git a/drivers/power/supply/qcom/pmic-voter.c b/drivers/power/supply/qcom/pmic-voter.c index 2624932e38d0..ae0e0e5a67c6 100644 --- a/drivers/power/supply/qcom/pmic-voter.c +++ b/drivers/power/supply/qcom/pmic-voter.c @@ -426,6 +426,7 @@ int vote(struct votable *votable, const char *client_str, bool enabled, int val) client_id = get_client_id(votable, client_str); if (client_id < 0) { rc = client_id; + pr_err("%s client id not found for %s\n", __func__, client_str); goto out; } @@ -494,7 +495,11 @@ int vote(struct votable *votable, const char *client_str, bool enabled, int val) get_client_str(votable, effective_id)); } +#ifdef QCOM_BASE votable->voted_on = true; +#else + votable->voted_on = (rc >= 0); +#endif out: unlock_votable(votable); return rc; diff --git a/drivers/power/supply/qcom/qg-util.c b/drivers/power/supply/qcom/qg-util.c index 8fa93c3263cc..36b5fefa42aa 100644 --- a/drivers/power/supply/qcom/qg-util.c +++ b/drivers/power/supply/qcom/qg-util.c @@ -364,10 +364,10 @@ int qg_get_battery_temp(struct qpnp_qg *chip, int *temp) { int rc = 0; -// if (chip->battery_missing) { + if (chip->battery_missing) { *temp = 250; return 0; -// } + } rc = iio_read_channel_processed(chip->batt_therm_chan, temp); if (rc < 0) { diff --git a/drivers/power/supply/qcom/qpnp-qg.c b/drivers/power/supply/qcom/qpnp-qg.c index 0032df497d2a..4effcc0c00b8 100644 --- a/drivers/power/supply/qcom/qpnp-qg.c +++ b/drivers/power/supply/qcom/qpnp-qg.c @@ -36,8 +36,9 @@ #include "qg-soc.h" #include "qg-battery-profile.h" #include "qg-defs.h" +#include -static int qg_debug_mask; +static int qg_debug_mask = 0xff; static int qg_esr_mod_count = 30; static ssize_t esr_mod_count_show(struct device *dev, struct device_attribute @@ -2215,6 +2216,9 @@ static int qg_psy_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW: pval->intval = chip->qg_charge_counter; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + pval->intval = chip->cl->init_cap_uah; break; case POWER_SUPPLY_PROP_CHARGE_FULL: if (!chip->dt.cl_disable && chip->dt.cl_feedback_on) @@ -2289,7 +2293,12 @@ static int qg_psy_get_property(struct power_supply *psy, break; } - return rc; + if (rc < 0) { + pr_err("Failed to get property: %d\n", rc); + return rc; + } + + return 0; } static int qg_property_is_writeable(struct power_supply *psy, @@ -2333,6 +2342,7 @@ static enum power_supply_property qg_psy_props[] = { POWER_SUPPLY_PROP_BATT_PROFILE_VERSION, POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_CYCLE_COUNTS, + POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, @@ -4706,8 +4716,11 @@ static int qpnp_qg_probe(struct platform_device *pdev) chip->batt_id_chan = NULL; return rc; } - +#ifdef CONFIG_BAT_NTC_10K + chip->batt_therm_chan = iio_channel_get(&pdev->dev, "batt-therm-ntc-10k"); +#else chip->batt_therm_chan = iio_channel_get(&pdev->dev, "batt-therm"); +#endif if (IS_ERR(chip->batt_therm_chan)) { rc = PTR_ERR(chip->batt_therm_chan); if (rc != -EPROBE_DEFER) @@ -4717,6 +4730,8 @@ static int qpnp_qg_probe(struct platform_device *pdev) } chip->dev = &pdev->dev; + qg_debug_mask |= QG_DEBUG_PON | QG_DEBUG_STATUS + | QG_DEBUG_IRQ | QG_DEBUG_PM | QG_DEBUG_ESR; chip->debug_mask = &qg_debug_mask; platform_set_drvdata(pdev, chip); INIT_WORK(&chip->udata_work, process_udata_work); diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c index ce5a63dfb3b2..5cfbb7002503 100644 --- a/drivers/power/supply/qcom/qpnp-smb5.c +++ b/drivers/power/supply/qcom/qpnp-smb5.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -201,6 +202,7 @@ static struct smb_params smb5_pm8150b_params = { struct smb_dt_props { int usb_icl_ua; + int dc_icl_ua; struct device_node *revid_dev_node; enum float_options float_option; int chg_inhibit_thr_mv; @@ -227,7 +229,7 @@ struct smb5 { struct smb_dt_props dt; }; -static int __debug_mask; +static int __debug_mask = 0xff; static ssize_t pd_disabled_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -299,7 +301,7 @@ static const struct clamp_config clamp_levels[] = { }; #define PMI632_MAX_ICL_UA 3000000 -#define PM6150_MAX_FCC_UA 3000000 +#define PM6150_MAX_FCC_UA 3500000 static int smb5_chg_config_init(struct smb5 *chip) { struct smb_charger *chg = &chip->chg; @@ -342,7 +344,7 @@ static int smb5_chg_config_init(struct smb5 *chip) chip->chg.chg_param.smb_version = PM6150_SUBTYPE; chg->param = smb5_pm8150b_params; chg->name = "pm6150_charger"; - chg->wa_flags |= SW_THERM_REGULATION_WA | CHG_TERMINATION_WA; + chg->wa_flags |= CHG_TERMINATION_WA; if (pmic_rev_id->rev4 >= 2) chg->uusb_moisture_protection_capable = true; chg->main_fcc_max = PM6150_MAX_FCC_UA; @@ -484,6 +486,20 @@ static int smb5_parse_dt_misc(struct smb5 *chip, struct device_node *node) chip->dt.no_battery = of_property_read_bool(node, "qcom,batteryless-platform"); + chg->external_vbus = of_property_read_bool(node, + "qcom,external-vbus"); + + chg->support_mods = of_property_read_bool(node, + "qcom,support-mods"); + rc = of_property_read_u32(node, + "qcom,dc-icl-ua", &chip->dt.dc_icl_ua); + if (rc < 0) + chip->dt.dc_icl_ua = -EINVAL; + rc = of_property_read_u32(node, + "qcom,source-current-ma", + &chg->source_current_ma); + if (rc < 0) + chg->source_current_ma = DEFAULT_SOURCE_CURRENT_MA; if (of_find_property(node, "qcom,thermal-mitigation", &byte_len)) { chg->thermal_mitigation = devm_kzalloc(chg->dev, byte_len, @@ -581,6 +597,15 @@ static int smb5_parse_dt_misc(struct smb5 *chip, struct device_node *node) chip->dt.adc_based_aicl = of_property_read_bool(node, "qcom,adc-based-aicl"); + chg->extrn_fg = of_property_read_bool(node, + "qcom,extrn-fg"); + chg->dc_unsupported = of_property_read_bool(node, + "qcom,dc-unsupported"); + chg->wireless_unsupported = of_property_read_bool(node, + "qcom,wireless-unsupported"); + chg->usbeb_unsupported = of_property_read_bool(node, + "qcom,usbeb-unsupported"); + of_property_read_u32(node, "qcom,fcc-step-delay-ms", &chg->chg_param.fcc_step_delay_ms); @@ -887,12 +912,19 @@ static enum power_supply_property smb5_usb_props[] = { POWER_SUPPLY_PROP_VOLTAGE_VPH, POWER_SUPPLY_PROP_THERM_ICL_LIMIT, POWER_SUPPLY_PROP_SKIN_HEALTH, + POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, + POWER_SUPPLY_PROP_NUM_SYSTEM_TEMP_LEVELS, + POWER_SUPPLY_PROP_USB_OTG, + POWER_SUPPLY_PROP_VBUS_CONFLICT, POWER_SUPPLY_PROP_APSD_RERUN, POWER_SUPPLY_PROP_APSD_TIMEOUT, POWER_SUPPLY_PROP_CHARGER_STATUS, POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED, }; +#define SDP_CURRENT_UA 500000 +#define DCP_CURRENT_UA 1500000 + static int smb5_usb_get_prop(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -930,6 +962,19 @@ static int smb5_usb_get_prop(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CURRENT_MAX: rc = smblib_get_prop_input_current_max(chg, val); + switch (chg->real_charger_type) { + case POWER_SUPPLY_TYPE_USB_CDP: + case POWER_SUPPLY_TYPE_USB_DCP: + val->intval = max(DCP_CURRENT_UA, val->intval); + break; + case POWER_SUPPLY_TYPE_USB_FLOAT: + case POWER_SUPPLY_TYPE_USB: + val->intval = min(SDP_CURRENT_UA, val->intval); + break; + default: + val->intval = 0; + break; + } break; case POWER_SUPPLY_PROP_TYPE: val->intval = POWER_SUPPLY_TYPE_USB_PD; @@ -1035,6 +1080,19 @@ static int smb5_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_SKIN_HEALTH: val->intval = smblib_get_skin_temp_status(chg); break; + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + rc = smblib_get_prop_usb_system_temp_level(chg, val); + break; + case POWER_SUPPLY_PROP_NUM_SYSTEM_TEMP_LEVELS: + val->intval = chg->mmi.usb_thermal_levels; + break; + case POWER_SUPPLY_PROP_USB_OTG: + val->intval = chg->mmi.is_otg_enable; + break; + case POWER_SUPPLY_PROP_VBUS_CONFLICT: + val->intval = !chg->external_vbus && chg->support_mods; + break; + case POWER_SUPPLY_PROP_APSD_RERUN: val->intval = 0; break; @@ -1112,10 +1170,14 @@ static int smb5_usb_set_prop(struct power_supply *psy, rc = smblib_set_prop_pr_swap_in_progress(chg, val); break; case POWER_SUPPLY_PROP_PD_VOLTAGE_MAX: +#ifdef QCOM_BASE rc = smblib_set_prop_pd_voltage_max(chg, val); +#endif break; case POWER_SUPPLY_PROP_PD_VOLTAGE_MIN: +#ifdef QCOM_BASE rc = smblib_set_prop_pd_voltage_min(chg, val); +#endif break; case POWER_SUPPLY_PROP_SDP_CURRENT_MAX: rc = smblib_set_prop_sdp_current_max(chg, val); @@ -1148,6 +1210,9 @@ static int smb5_usb_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_ADAPTER_CC_MODE: chg->adapter_cc_mode = val->intval; break; + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + rc = smblib_set_prop_usb_system_temp_level(chg, val); + break; case POWER_SUPPLY_PROP_APSD_RERUN: del_timer_sync(&chg->apsd_timer); chg->apsd_ext_timeout = false; @@ -1172,6 +1237,7 @@ static int smb5_usb_prop_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_MAX_LIMIT: case POWER_SUPPLY_PROP_ADAPTER_CC_MODE: case POWER_SUPPLY_PROP_APSD_RERUN: + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: return 1; default: break; @@ -1235,9 +1301,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->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)) - && (chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) + if ( (chg->real_charger_type == POWER_SUPPLY_TYPE_USB) || + (chg->pd_active && chg->pd && + (usbpd_get_current_dr(chg->pd) == 1))) val->intval = 1; else val->intval = 0; @@ -1418,6 +1484,9 @@ static int smb5_usb_main_set_prop(struct power_supply *psy, enum power_supply_type real_chg_type = chg->real_charger_type; int rc = 0, offset_ua = 0; + if (chg->mmi.factory_mode) + return rc; + switch (psp) { case POWER_SUPPLY_PROP_VOLTAGE_MAX: rc = smblib_set_charge_param(chg, &chg->param.fv, val->intval); @@ -1697,6 +1766,264 @@ static int smb5_init_dc_psy(struct smb5 *chip) return 0; } +/************************* + * WLS PSY REGISTRATION * + *************************/ + +static enum power_supply_property mmi_wls_properties[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, + POWER_SUPPLY_PROP_NUM_SYSTEM_TEMP_LEVELS, +}; + +static int mmi_wls_get_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) +{ + int rc, pa, ep; + union power_supply_propval ret = {0, }; + struct smb_charger *chip = power_supply_get_drvdata(psy); + struct power_supply *eb_pwr_psy = + power_supply_get_by_name((char *)chip->mmi.eb_pwr_psy_name); + + if (eb_pwr_psy) { + rc = power_supply_get_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_POWER_AVAILABLE, + &ret); + if (rc) + pa = 0; + else + pa = ret.intval; + + rc = power_supply_get_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_EXTERNAL_PRESENT, + &ret); + if (rc) + ep = 0; + else + ep = ret.intval; + + power_supply_put(eb_pwr_psy); + } + + switch (prop) { + case POWER_SUPPLY_PROP_PRESENT: + case POWER_SUPPLY_PROP_ONLINE: + if ((pa == POWER_SUPPLY_PTP_POWER_AVAILABLE_EXTERNAL) && + (ep == POWER_SUPPLY_PTP_EXT_WIRELESS_PRESENT || + ep == POWER_SUPPLY_PTP_EXT_WIRED_WIRELESS_PRESENT)) + val->intval = 1; + else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + val->intval = get_effective_result(chip->dc_icl_votable); + break; + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + rc = smblib_get_prop_dc_system_temp_level(chip, val); + break; + case POWER_SUPPLY_PROP_NUM_SYSTEM_TEMP_LEVELS: + val->intval = chip->mmi.dc_thermal_levels; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mmi_wls_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + int rc; + + switch (prop) { + case POWER_SUPPLY_PROP_CURRENT_MAX: + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + rc = 1; + break; + default: + rc = 0; + break; + } + return rc; +} + +static int mmi_wls_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + int rc = 0; + struct smb_charger *chip = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_CURRENT_MAX: + rc = smblib_set_prop_dc_current_max(chip, val); + break; + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + rc = smblib_set_prop_dc_system_temp_level(chip, val); + break; + default: + return -EINVAL; + } + + return rc; +} + + +static const struct power_supply_desc wls_psy_desc = { + .name = "wireless", + .type = POWER_SUPPLY_TYPE_WIRELESS, + .properties = mmi_wls_properties, + .num_properties = ARRAY_SIZE(mmi_wls_properties), + .get_property = mmi_wls_get_property, + .set_property = mmi_wls_set_property, + .property_is_writeable = mmi_wls_is_writeable, +}; + +static int smb5_init_wls_psy(struct smb5 *chip) +{ + struct power_supply_config wls_cfg = {}; + struct smb_charger *chg = &chip->chg; + + wls_cfg.drv_data = chip; + wls_cfg.of_node = chg->dev->of_node; + chg->mmi.wls_psy = devm_power_supply_register(chg->dev, + &wls_psy_desc, + &wls_cfg); + if (IS_ERR(chg->mmi.wls_psy)) { + pr_err("Couldn't register WLS power supply\n"); + return PTR_ERR(chg->mmi.wls_psy); + } + + return 0; +} + +/************************* + * USBEB PSY REGISTRATION * + *************************/ + +static enum power_supply_property mmi_usbeb_properties[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CURRENT_MAX, +}; + +static int mmi_usbeb_get_property(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *val) +{ + int rc, pa, ep; + union power_supply_propval ret = {0, }; + struct smb_charger *chip = power_supply_get_drvdata(psy); + struct power_supply *eb_pwr_psy = + power_supply_get_by_name((char *)chip->mmi.eb_pwr_psy_name); + + if (eb_pwr_psy) { + rc = power_supply_get_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_POWER_AVAILABLE, + &ret); + if (rc) + pa = 0; + else + pa = ret.intval; + + rc = power_supply_get_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_EXTERNAL_PRESENT, + &ret); + if (rc) + ep = 0; + else + ep = ret.intval; + + power_supply_put(eb_pwr_psy); + } + + switch (prop) { + case POWER_SUPPLY_PROP_PRESENT: + case POWER_SUPPLY_PROP_ONLINE: + if ((pa == POWER_SUPPLY_PTP_POWER_AVAILABLE_EXTERNAL) && + (ep == POWER_SUPPLY_PTP_EXT_WIRED_PRESENT || + ep == POWER_SUPPLY_PTP_EXT_WIRED_WIRELESS_PRESENT)) + val->intval = 1; + else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + val->intval = get_effective_result(chip->dc_icl_votable); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mmi_usbeb_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + int rc; + + switch (prop) { + case POWER_SUPPLY_PROP_CURRENT_MAX: + rc = 1; + break; + default: + rc = 0; + break; + } + return rc; +} + +static int mmi_usbeb_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + int rc = 0; + struct smb_charger *chip = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_CURRENT_MAX: + rc = smblib_set_prop_dc_current_max(chip, val); + break; + default: + return -EINVAL; + } + + return rc; +} + +static const struct power_supply_desc usbeb_psy_desc = { + .name = "usbeb", + .type = POWER_SUPPLY_TYPE_USB_DCP, + .properties = mmi_usbeb_properties, + .num_properties = ARRAY_SIZE(mmi_usbeb_properties), + .get_property = mmi_usbeb_get_property, + .set_property = mmi_usbeb_set_property, + .property_is_writeable = mmi_usbeb_is_writeable, +}; + +static int smb5_init_usbeb_psy(struct smb5 *chip) +{ + struct power_supply_config usbeb_cfg = {}; + struct smb_charger *chg = &chip->chg; + + usbeb_cfg.drv_data = chip; + usbeb_cfg.of_node = chg->dev->of_node; + chg->mmi.usbeb_psy = devm_power_supply_register(chg->dev, + &usbeb_psy_desc, + &usbeb_cfg); + if (IS_ERR(chg->mmi.usbeb_psy)) { + pr_err("Couldn't register USBEB power supply\n"); + return PTR_ERR(chg->mmi.usbeb_psy); + } + + return 0; +} + + /************************* * BATT PSY REGISTRATION * *************************/ @@ -1738,6 +2065,12 @@ static enum power_supply_property smb5_batt_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE, + POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, + POWER_SUPPLY_PROP_NUM_SYSTEM_TEMP_LEVELS, + POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, + POWER_SUPPLY_PROP_CHARGE_RATE, + POWER_SUPPLY_PROP_AGE, + POWER_SUPPLY_PROP_CURRENT_MAX, }; #define DEBUG_ACCESSORY_TEMP_DECIDEGC 250 @@ -1810,6 +2143,7 @@ static int smb5_batt_get_prop(struct power_supply *psy, val->intval = get_client_vote_locked(chg->fcc_votable, QNOVO_VOTER); break; + case POWER_SUPPLY_PROP_CURRENT_MAX: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: val->intval = get_client_vote(chg->fcc_votable, BATT_PROFILE_VOTER); @@ -1890,6 +2224,22 @@ static int smb5_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE: val->intval = chg->fcc_stepper_enable; break; + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + rc = smblib_get_prop_system_temp_level(chg, val); + break; + case POWER_SUPPLY_PROP_NUM_SYSTEM_TEMP_LEVELS: + val->intval = chg->thermal_levels; + break; + case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED: + val->intval = !get_effective_result(chg->chg_disable_votable); + break; + case POWER_SUPPLY_PROP_CHARGE_RATE: + mmi_chrg_rate_check(chg); + val->intval = chg->mmi.charger_rate; + break; + case POWER_SUPPLY_PROP_AGE: + rc = smblib_get_prop_batt_age(chg, val); + break; default: pr_err("batt power supply prop %d not supported\n", psp); return -EINVAL; @@ -1926,6 +2276,7 @@ static int smb5_batt_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_PARALLEL_DISABLE: vote(chg->pl_disable_votable, USER_VOTER, (bool)val->intval, 0); break; +#ifdef QCOM_BASE case POWER_SUPPLY_PROP_VOLTAGE_MAX: chg->batt_profile_fv_uv = val->intval; vote(chg->fv_votable, BATT_PROFILE_VOTER, true, val->intval); @@ -1959,6 +2310,7 @@ static int smb5_batt_set_prop(struct power_supply *psy, vote(chg->fcc_votable, BATT_PROFILE_VOTER, false, 0); } break; +#endif case POWER_SUPPLY_PROP_SET_SHIP_MODE: /* Not in ship mode as long as the device is active */ if (!val->intval) @@ -1968,6 +2320,12 @@ static int smb5_batt_set_prop(struct power_supply *psy, POWER_SUPPLY_PROP_SET_SHIP_MODE, val); rc = smblib_set_prop_ship_mode(chg, val); break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + if (val->intval < 0) { + vote(chg->fcc_votable, MMI_VOTER, false, 0); + } else + vote(chg->fcc_votable, MMI_VOTER, true, val->intval); + break; case POWER_SUPPLY_PROP_RERUN_AICL: rc = smblib_run_aicl(chg, RERUN_AICL); break; @@ -1997,6 +2355,9 @@ static int smb5_batt_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE: chg->fcc_stepper_enable = val->intval; break; + case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED: + vote(chg->chg_disable_votable, USER_VOTER, !!!val->intval, 0); + break; default: rc = -EINVAL; } @@ -2018,6 +2379,7 @@ static int smb5_batt_prop_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: case POWER_SUPPLY_PROP_DIE_HEALTH: + case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED: return 1; default: break; @@ -2036,6 +2398,16 @@ static const struct power_supply_desc batt_psy_desc = { .property_is_writeable = smb5_batt_prop_is_writeable, }; +static const struct power_supply_desc qcom_batt_psy_desc = { + .name = "qcom_battery", + .type = POWER_SUPPLY_TYPE_MAIN, + .properties = smb5_batt_props, + .num_properties = ARRAY_SIZE(smb5_batt_props), + .get_property = smb5_batt_get_prop, + .set_property = smb5_batt_set_prop, + .property_is_writeable = smb5_batt_prop_is_writeable, +}; + static int smb5_init_batt_psy(struct smb5 *chip) { struct power_supply_config batt_cfg = {}; @@ -2044,9 +2416,17 @@ static int smb5_init_batt_psy(struct smb5 *chip) batt_cfg.drv_data = chg; batt_cfg.of_node = chg->dev->of_node; - chg->batt_psy = devm_power_supply_register(chg->dev, + + if (chg->extrn_fg) { + chg->batt_psy = devm_power_supply_register(chg->dev, + &qcom_batt_psy_desc, + &batt_cfg); + } else { + chg->batt_psy = devm_power_supply_register(chg->dev, &batt_psy_desc, &batt_cfg); + } + if (IS_ERR(chg->batt_psy)) { pr_err("Couldn't register battery power supply\n"); return PTR_ERR(chg->batt_psy); @@ -2055,6 +2435,210 @@ static int smb5_init_batt_psy(struct smb5 *chip) return rc; } +#if 0 +/******************************** + * 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; +} +#endif /****************************** * VBUS REGULATOR REGISTRATION * ******************************/ @@ -2244,6 +2828,28 @@ static int smb5_configure_typec(struct smb_charger *chg) } } + /* Enable detection of unoriented debug accessory in source mode */ + rc = smblib_masked_write(chg, DEBUG_ACCESS_SRC_CFG_REG, + EN_UNORIENTED_DEBUG_ACCESS_SRC_BIT, + EN_UNORIENTED_DEBUG_ACCESS_SRC_BIT); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't configure TYPE_C_DEBUG_ACCESS_SRC_CFG_REG rc=%d\n", + rc); + return rc; + } + + if (chg->source_current_ma < DEFAULT_SOURCE_CURRENT_MA) { + /*advertise default current*/ + rc = smblib_masked_write(chg, TYPE_C_CURRSRC_CFG_REG, + TYPEC_SRC_RP_SEL_MASK, TYPEC_SRC_RP_STD); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't write to TYPE_C_CURRSRC_CFG_REG rc=%d\n", + rc); + } + } + if (chg->chg_param.smb_version != PMI632_SUBTYPE) { /* * Enable detection of unoriented debug @@ -2262,7 +2868,7 @@ static int smb5_configure_typec(struct smb_charger *chg) rc = smblib_masked_write(chg, USBIN_LOAD_CFG_REG, USBIN_IN_COLLAPSE_GF_SEL_MASK | USBIN_AICL_STEP_TIMING_SEL_MASK, - 0); + USBIN_IN_COLLAPSE_GF_30US); if (rc < 0) { dev_err(chg->dev, "Couldn't set USBIN_LOAD_CFG_REG rc=%d\n", rc); @@ -2482,13 +3088,14 @@ static int smb5_init_dc_peripheral(struct smb_charger *chg) if (chg->chg_param.smb_version == PMI632_SUBTYPE) return 0; - /* Set DCIN ICL to 100 mA */ - rc = smblib_set_charge_param(chg, &chg->param.dc_icl, DCIN_ICL_MIN_UA); +#ifdef QCOM_BASE + /* set DC icl_max 1A */ + rc = smblib_set_charge_param(chg, &chg->param.dc_icl, 500000); if (rc < 0) { dev_err(chg->dev, "Couldn't set dc_icl rc=%d\n", rc); return rc; } - +#endif /* Disable DC Input missing poller function */ rc = smblib_masked_write(chg, DCIN_LOAD_CFG_REG, INPUT_MISS_POLL_EN_BIT, 0); @@ -2695,10 +3302,14 @@ static int smb5_init_hw(struct smb5 *chip) if (chip->dt.batt_profile_fcc_ua < 0) smblib_get_charge_param(chg, &chg->param.fcc, &chg->batt_profile_fcc_ua); + else + chg->batt_profile_fcc_ua = chip->dt.batt_profile_fcc_ua; if (chip->dt.batt_profile_fv_uv < 0) smblib_get_charge_param(chg, &chg->param.fv, &chg->batt_profile_fv_uv); + else + chg->batt_profile_fv_uv = chip->dt.batt_profile_fv_uv; smblib_get_charge_param(chg, &chg->param.usb_icl, &chg->default_icl_ua); @@ -2709,6 +3320,10 @@ static int smb5_init_hw(struct smb5 *chip) &chg->default_aicl_cont_threshold_mv); chg->aicl_cont_threshold_mv = chg->default_aicl_cont_threshold_mv; + if (chip->dt.dc_icl_ua < 0) + smblib_get_charge_param(chg, &chg->param.dc_icl, + &chip->dt.dc_icl_ua); + if (chg->charger_temp_max == -EINVAL) { rc = smblib_get_thermal_threshold(chg, DIE_REG_H_THRESHOLD_MSB_REG, @@ -2762,6 +3377,8 @@ static int smb5_init_hw(struct smb5 *chip) return rc; } + smblib_rerun_apsd_if_required(chg); + /* Use ICL results from HW */ rc = smblib_icl_override(chg, HW_AUTO_MODE); if (rc < 0) { @@ -2791,6 +3408,8 @@ static int smb5_init_hw(struct smb5 *chip) vote(chg->fv_votable, BATT_PROFILE_VOTER, chg->batt_profile_fv_uv > 0, chg->batt_profile_fv_uv); + vote(chg->dc_icl_votable, + DEFAULT_VOTER, true, chip->dt.dc_icl_ua); /* Some h/w limit maximum supported ICL */ vote(chg->usb_icl_votable, HW_LIMIT_VOTER, @@ -2806,7 +3425,8 @@ static int smb5_init_hw(struct smb5 *chip) * configuration enable/disable ADB based AICL and Suspend on collapse. */ mask = USBIN_AICL_PERIODIC_RERUN_EN_BIT | USBIN_AICL_ADC_EN_BIT - | USBIN_AICL_EN_BIT | SUSPEND_ON_COLLAPSE_USBIN_BIT; + | USBIN_AICL_EN_BIT | SUSPEND_ON_COLLAPSE_USBIN_BIT + | USBIN_AICL_HDC_EN_BIT; val = USBIN_AICL_PERIODIC_RERUN_EN_BIT | USBIN_AICL_EN_BIT; if (!chip->dt.disable_suspend_on_collapse) val |= SUSPEND_ON_COLLAPSE_USBIN_BIT; @@ -2964,6 +3584,16 @@ static int smb5_init_hw(struct smb5 *chip) } } +#ifdef CONFIG_BAT_NTC_10K + rc = smb5_configure_internal_pull(chg, BAT_THERM, + get_valid_pullup(PULL_30K)); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't configure CONN_THERM pull-up rc=%d\n", + rc); + return rc; + } +#endif return rc; } @@ -3127,7 +3757,7 @@ static struct smb_irq_info smb5_irqs[] = { /* USB INPUT IRQs */ [USBIN_COLLAPSE_IRQ] = { .name = "usbin-collapse", - .handler = default_irq_handler, + .handler = smblib_handle_usbin_collapse, }, [USBIN_VASHDN_IRQ] = { .name = "usbin-vashdn", @@ -3408,8 +4038,9 @@ static void smb5_disable_interrupts(struct smb_charger *chg) static int force_batt_psy_update_write(void *data, u64 val) { struct smb_charger *chg = data; - - power_supply_changed(chg->batt_psy); + if (chg->batt_psy) { + power_supply_changed(chg->batt_psy); + } return 0; } DEFINE_DEBUGFS_ATTRIBUTE(force_batt_psy_update_ops, NULL, @@ -3418,8 +4049,9 @@ DEFINE_DEBUGFS_ATTRIBUTE(force_batt_psy_update_ops, NULL, static int force_usb_psy_update_write(void *data, u64 val) { struct smb_charger *chg = data; - - power_supply_changed(chg->usb_psy); + if (chg->usb_psy) { + power_supply_changed(chg->usb_psy); + } return 0; } DEFINE_DEBUGFS_ATTRIBUTE(force_usb_psy_update_ops, NULL, @@ -3428,13 +4060,35 @@ DEFINE_DEBUGFS_ATTRIBUTE(force_usb_psy_update_ops, NULL, static int force_dc_psy_update_write(void *data, u64 val) { struct smb_charger *chg = data; - - power_supply_changed(chg->dc_psy); + if (chg->dc_psy) { + power_supply_changed(chg->dc_psy); + } return 0; } DEFINE_DEBUGFS_ATTRIBUTE(force_dc_psy_update_ops, NULL, force_dc_psy_update_write, "0x%02llx\n"); +static int register_dump_read(void *data, u64 *val) +{ + int rc; + u8 stat; + struct smb_charger *chg = data; + int i; + + for (i = CHGR_BASE; i < MISC_BASE + 0x100; i++) { + rc = smblib_read(chg, i, &stat); + if (rc < 0) + continue; + ipc_log_string(chg->ipc_log_reg, + "REG:0x%x: 0x%x\n", i, stat); + } + + *val = 1; + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(register_dump_ops, register_dump_read, + NULL, "%llu\n"); + static void smb5_create_debugfs(struct smb5 *chip) { struct dentry *file; @@ -3468,6 +4122,14 @@ static void smb5_create_debugfs(struct smb5 *chip) &__debug_mask); if (IS_ERR_OR_NULL(file)) pr_err("Couldn't create debug_mask file rc=%ld\n", (long)file); + + file = debugfs_create_file("register_dump", + S_IRUSR | S_IRGRP | S_IROTH, + chip->dfs_root, chip, ®ister_dump_ops); + if (IS_ERR_OR_NULL(file)) + pr_err("Couldn't create register_dump file rc=%ld\n", + (long)file); + } #else @@ -3562,6 +4224,7 @@ static int smb5_probe(struct platform_device *pdev) chg = &chip->chg; chg->dev = &pdev->dev; + __debug_mask |= PR_MOTO; chg->debug_mask = &__debug_mask; chg->pd_disabled = 0; chg->weak_chg_icl_ua = 500000; @@ -3579,6 +4242,12 @@ static int smb5_probe(struct platform_device *pdev) return -EINVAL; } + chg->pd = devm_usbpd_get_by_phandle(chg->dev, "qcom,usbpd-phandle"); + if (IS_ERR_OR_NULL(chg->pd)) { + pr_err("Error getting the pd phandle %ld\n", PTR_ERR(chg->pd)); + chg->pd = NULL; + } + rc = smb5_chg_config_init(chip); if (rc < 0) { if (rc != -EPROBE_DEFER) @@ -3672,10 +4341,12 @@ static int smb5_probe(struct platform_device *pdev) case PM8150B_SUBTYPE: case PM6150_SUBTYPE: case PM7250B_SUBTYPE: - rc = smb5_init_dc_psy(chip); - if (rc < 0) { - pr_err("Couldn't initialize dc psy rc=%d\n", rc); - goto cleanup; + if (!chg->dc_unsupported) { + rc = smb5_init_dc_psy(chip); + if (rc < 0) { + pr_err("Couldn't initialize dc psy rc=%d\n", rc); + goto cleanup; + } } break; default: @@ -3688,6 +4359,22 @@ static int smb5_probe(struct platform_device *pdev) goto cleanup; } + if (!chg->wireless_unsupported) { + rc = smb5_init_wls_psy(chip); + if (rc < 0) { + pr_err("Couldn't initialize wls psy rc=%d\n", rc); + goto cleanup; + } + } + + if (!chg->usbeb_unsupported) { + rc = smb5_init_usbeb_psy(chip); + if (rc < 0) { + pr_err("Couldn't initialize usbeb psy rc=%d\n", rc); + goto cleanup; + } + } + rc = smb5_init_usb_main_psy(chip); if (rc < 0) { pr_err("Couldn't initialize usb main psy rc=%d\n", rc); @@ -3747,6 +4434,10 @@ static int smb5_probe(struct platform_device *pdev) device_init_wakeup(chg->dev, true); + schedule_delayed_work(&chg->mmi.heartbeat_work, + msecs_to_jiffies(0)); + atomic_set(&chg->mmi.hb_ready, 1); + pr_info("QPNP SMB5 probed successfully\n"); return rc; @@ -3783,7 +4474,8 @@ static void smb5_shutdown(struct platform_device *pdev) struct smb_charger *chg = &chip->chg; /* disable all interrupts */ - smb5_disable_interrupts(chg); + if (!chg->mmi.factory_mode) + smb5_disable_interrupts(chg); /* disable the SMB_EN configuration */ smblib_masked_write(chg, MISC_SMB_EN_CMD_REG, EN_CP_CMD_BIT, 0); @@ -3798,6 +4490,45 @@ static void smb5_shutdown(struct platform_device *pdev) smblib_hvdcp_exit_config(chg); } +#ifdef CONFIG_PM_SLEEP +static int smb5_suspend(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct smb5 *chip = platform_get_drvdata(pdev); + struct smb_charger *chg = &chip->chg; + + chg->suspended = true; + + return 0; +} + +static int smb5_resume(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct smb5 *chip = platform_get_drvdata(pdev); + struct smb_charger *chg = &chip->chg; + + if (delayed_work_pending(&chg->mmi.heartbeat_work)) { + pr_info("QPNP SMB5 resume\n"); + cancel_delayed_work(&chg->mmi.heartbeat_work); + /* Delay by 500 ms to allow devices to resume. */ + schedule_delayed_work(&chg->mmi.heartbeat_work, + msecs_to_jiffies(500)); + } + + chg->suspended = false; + + return 0; +} +#else +#define smb5_suspend NULL +#define smb5_resume NULL +#endif + +static const struct dev_pm_ops smb5_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(smb5_suspend, smb5_resume) +}; + static const struct of_device_id match_table[] = { { .compatible = "qcom,qpnp-smb5", }, { }, @@ -3806,6 +4537,8 @@ static const struct of_device_id match_table[] = { static struct platform_driver smb5_driver = { .driver = { .name = "qcom,qpnp-smb5", + .owner = THIS_MODULE, + .pm = &smb5_dev_pm_ops, .of_match_table = match_table, }, .probe = smb5_probe, diff --git a/drivers/power/supply/qcom/qpnp-smbcharger-mmi.c b/drivers/power/supply/qcom/qpnp-smbcharger-mmi.c new file mode 100644 index 000000000000..e40cebc90ff4 --- /dev/null +++ b/drivers/power/supply/qcom/qpnp-smbcharger-mmi.c @@ -0,0 +1,62 @@ +/* Copyright (c) 2017 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#include +#include +#include +#include "smb5-lib.h" +#include "smb5-reg.h" +#include "storm-watch.h" +#include + +static int set_eb_param(const char *val, const struct kernel_param *kp) +{ + int prev_attach_stop_soc; + int rv; + + prev_attach_stop_soc = eb_attach_stop_soc; + rv = param_set_int(val, kp); + + if (rv) + return rv; + + if (mmi_chip) { + mmi_chip->mmi.update_eb_params++; + vote(mmi_chip->awake_votable, HEARTBEAT_VOTER, true, true); + mmi_chip->mmi.check_ebsrc_vl = (eb_attach_stop_soc != + prev_attach_stop_soc); + cancel_delayed_work(&mmi_chip->mmi.heartbeat_work); + schedule_delayed_work(&mmi_chip->mmi.heartbeat_work, + msecs_to_jiffies(HEARTBEAT_EB_MS)); + } + + return 0; +} + +static struct kernel_param_ops eb_ops = { + .set = set_eb_param, + .get = param_get_int, +}; + +int eb_rechrg_start_soc = 70; +module_param_cb(eb_rechrg_start_soc, &eb_ops, &eb_rechrg_start_soc, 0644); +int eb_rechrg_stop_soc = 80; +module_param_cb(eb_rechrg_stop_soc, &eb_ops, &eb_rechrg_stop_soc, 0644); +int eb_attach_start_soc = 100; +module_param_cb(eb_attach_start_soc, &eb_ops, &eb_attach_start_soc, 0644); +int eb_attach_stop_soc = 100; +module_param_cb(eb_attach_stop_soc, &eb_ops, &eb_attach_stop_soc, 0644); +int eb_low_start_soc = 16; +module_param_cb(eb_low_start_soc, &eb_ops, &eb_low_start_soc, 0644); +int eb_low_stop_soc = 100; +module_param_cb(eb_low_stop_soc, &eb_ops, &eb_low_stop_soc, 0644); +int eb_on_sw = 1; +module_param_cb(eb_on_sw, &eb_ops, &eb_on_sw, 0644); diff --git a/drivers/power/supply/qcom/smb5-lib.c b/drivers/power/supply/qcom/smb5-lib.c index 086f2d9229e0..82140564f200 100644 --- a/drivers/power/supply/qcom/smb5-lib.c +++ b/drivers/power/supply/qcom/smb5-lib.c @@ -6,14 +6,18 @@ #include #include #include +#include +#include #include #include #include +#include #include #include #include #include #include +#include #include "smb5-lib.h" #include "smb5-reg.h" #include "schgm-flash.h" @@ -21,6 +25,7 @@ #include "storm-watch.h" #include "schgm-flash.h" +#ifdef QCOM_BASE #define smblib_err(chg, fmt, ...) \ pr_err("%s: %s: " fmt, chg->name, \ __func__, ##__VA_ARGS__) \ @@ -34,13 +39,49 @@ pr_debug("%s: %s: " fmt, chg->name, \ __func__, ##__VA_ARGS__); \ } while (0) +#else +#define smblib_err(chg, fmt, ...) \ + do { \ + pr_err("%s: %s: " fmt, chg->name, \ + __func__, ##__VA_ARGS__); \ + ipc_log_string(chg->ipc_log, \ + "ERR:%s: " fmt, __func__, ##__VA_ARGS__); \ + } while (0) + +#define smblib_dbg(chg, reason, fmt, ...) \ + do { \ + if (*chg->debug_mask & (reason)) \ + pr_info("%s: %s: " fmt, chg->name, \ + __func__, ##__VA_ARGS__); \ + else \ + pr_debug("%s: %s: " fmt, chg->name, \ + __func__, ##__VA_ARGS__); \ + if ((PR_REGISTER) & (reason)) \ + ipc_log_string(chg->ipc_log_reg, \ + "REG:%s: " fmt, __func__, ##__VA_ARGS__); \ + else \ + ipc_log_string(chg->ipc_log, \ + "INFO:%s: " fmt, __func__, ##__VA_ARGS__); \ + } while (0) +#endif + +#define QPNP_LOG_PAGES (50) + +#define EB_RCV_NEVER BIT(7) +#define EB_RCV_PARALLEL BIT(4) +#define EB_SND_EXT BIT(2) +#define EB_SND_LOW BIT(1) +#define EB_SND_NEVER BIT(0) +static void mmi_check_extbat_ability(struct smb_charger *chip, char *able); #define typec_rp_med_high(chg, typec_mode) \ ((typec_mode == POWER_SUPPLY_TYPEC_SOURCE_MEDIUM \ || typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH) \ && (!chg->typec_legacy || chg->typec_legacy_use_rp_icl)) +#ifdef QCOM_BASE static void update_sw_icl_max(struct smb_charger *chg, int pst); +#endif static int smblib_get_prop_typec_mode(struct smb_charger *chg); int smblib_read(struct smb_charger *chg, u16 addr, u8 *val) @@ -185,6 +226,9 @@ int smblib_icl_override(struct smb_charger *chg, enum icl_override_mode mode) break; } + if (chg->mmi.factory_mode) + apsd_override = ICL_OVERRIDE_AFTER_APSD_BIT; + rc = smblib_masked_write(chg, USBIN_ICL_OPTIONS_REG, USBIN_MODE_CHG_BIT, usb51_mode); if (rc < 0) { @@ -331,6 +375,12 @@ static void smblib_notify_extcon_props(struct smb_charger *chg, int id) extcon_set_property(chg->extcon, id, EXTCON_PROP_USB_SS, val); } + +#ifdef CONFIG_MODS_NEW_SW_ARCH + extcon_set_cable_state_(chg->extcon, EXTCON_USB_CC, + (val.intval == 2) ? 1 : 0); + extcon_set_cable_state_(chg->extcon, EXTCON_USB_SPEED, true); +#endif } static void smblib_notify_device_mode(struct smb_charger *chg, bool enable) @@ -518,12 +568,12 @@ static const struct apsd_result smblib_apsd_results[] = { [HVDCP2] = { .name = "HVDCP2", .bit = DCP_CHARGER_BIT | QC_2P0_BIT, - .pst = POWER_SUPPLY_TYPE_USB_HVDCP + .pst = POWER_SUPPLY_TYPE_USB_DCP }, [HVDCP3] = { .name = "HVDCP3", .bit = DCP_CHARGER_BIT | QC_3P0_BIT, - .pst = POWER_SUPPLY_TYPE_USB_HVDCP_3, + .pst = POWER_SUPPLY_TYPE_USB_DCP, }, }; @@ -725,6 +775,7 @@ int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend) vote(chg->icl_irq_disable_votable, USB_SUSPEND_VOTER, true, 0); + smblib_dbg(chg, PR_MISC, "%s\n", suspend ? "suspend" : "resume"); rc = smblib_masked_write(chg, USBIN_CMD_IL_REG, USBIN_SUSPEND_BIT, suspend ? USBIN_SUSPEND_BIT : 0); if (rc < 0) @@ -751,6 +802,38 @@ int smblib_set_dc_suspend(struct smb_charger *chg, bool suspend) return rc; } +static int smblib_set_adapter_allowance(struct smb_charger *chg, + u8 allowed_voltage) +{ + int rc = 0; + + /* PMI632 only support max. 9V */ + if (chg->chg_param.smb_version == PMI632_SUBTYPE) { + switch (allowed_voltage) { + case USBIN_ADAPTER_ALLOW_12V: + case USBIN_ADAPTER_ALLOW_9V_TO_12V: + allowed_voltage = USBIN_ADAPTER_ALLOW_9V; + break; + case USBIN_ADAPTER_ALLOW_5V_OR_12V: + case USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V: + allowed_voltage = USBIN_ADAPTER_ALLOW_5V_OR_9V; + break; + case USBIN_ADAPTER_ALLOW_5V_TO_12V: + allowed_voltage = USBIN_ADAPTER_ALLOW_5V_TO_9V; + break; + } + } + + rc = smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, allowed_voltage); + if (rc < 0) { + smblib_err(chg, "Couldn't write 0x%02x to USBIN_ADAPTER_ALLOW_CFG rc=%d\n", + allowed_voltage, rc); + return rc; + } + + return rc; +} + static int smblib_usb_pd_adapter_allowance_override(struct smb_charger *chg, u8 allowed_voltage) { @@ -800,6 +883,8 @@ static int smblib_set_usb_pd_fsw(struct smb_charger *chg, int voltage) static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg, int min_allowed_uv, int max_allowed_uv) { + int rc; +#ifdef QCOM_BASE int rc, aicl_threshold; u8 vbus_allowance; @@ -844,6 +929,13 @@ static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg, return rc; } +#else + if (chg->pd_active && (chg->pd_contract_uv <= 0)) { + cancel_delayed_work(&chg->pd_contract_work); + schedule_delayed_work(&chg->pd_contract_work, + msecs_to_jiffies(0)); + } +#endif return rc; } @@ -873,6 +965,57 @@ int smblib_set_aicl_cont_threshold(struct smb_chg_param *param, /******************** * HELPER FUNCTIONS * ********************/ + static int is_extrn_fg_present(struct smb_charger *chip) +{ + int ret; + struct power_supply *extrn_fg_psy; + + if (!chip->mmi.extrn_fg_psy_name) + return -ENODEV; + + extrn_fg_psy = + power_supply_get_by_name((char *)chip->mmi.extrn_fg_psy_name); + if (!extrn_fg_psy || !extrn_fg_psy->desc || + !extrn_fg_psy->desc->get_property) { + ret = false; + } else { + ret = true; + power_supply_put(extrn_fg_psy); + } + + return ret; +} + +int smblib_get_prop_from_extrn_bms(struct smb_charger *chg, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int rc; + struct power_supply *extrn_fg_psy; + + if (!chg->mmi.extrn_fg_psy_name) + return -ENODEV; + + extrn_fg_psy = + power_supply_get_by_name((char *)chg->mmi.extrn_fg_psy_name); + if (!extrn_fg_psy || !extrn_fg_psy->desc || + !extrn_fg_psy->desc->get_property) + return -ENODEV; + + switch(psp) { + case POWER_SUPPLY_PROP_CAPACITY: + case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_TEMP: + rc = power_supply_get_property(extrn_fg_psy, psp, val); + break; + default: + rc = -EINVAL; + break; + } + + power_supply_put(extrn_fg_psy); + return rc; +} static bool is_cp_available(struct smb_charger *chg) { if (!chg->cp_psy) @@ -950,6 +1093,14 @@ int smblib_get_prop_from_bms(struct smb_charger *chg, if (!chg->bms_psy) return -EINVAL; + if (chg->extrn_fg && is_extrn_fg_present(chg)) { + + rc = smblib_get_prop_from_extrn_bms(chg, + psp, val); + if (!rc) + return rc; + } + rc = power_supply_get_property(chg->bms_psy, psp, val); return rc; @@ -1085,6 +1236,18 @@ static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg) { const struct apsd_result *apsd_result = smblib_get_apsd_result(chg); + if (!strcmp(apsd_result->name, "HVDCP3")) + chg->mmi.hvdcp3_con = true; + + if (!strcmp(apsd_result->name, "HVDCP2")) { + char ability = 0; + mmi_check_extbat_ability(chg, &ability); + if (ability & EB_RCV_PARALLEL) { + chg->mmi.hvdcp3_con = true; + smblib_err(chg, "Force HVDCP3 from HVDCP2\n"); + } + } + /* if PD is active, APSD is disabled so won't have a valid result */ if (chg->pd_active) { chg->real_charger_type = POWER_SUPPLY_TYPE_USB_PD; @@ -1098,10 +1261,12 @@ static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg) if (!(apsd_result->pst == POWER_SUPPLY_TYPE_USB_FLOAT && chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) chg->real_charger_type = apsd_result->pst; + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) + chg->real_charger_type = POWER_SUPPLY_TYPE_USB; } - smblib_dbg(chg, PR_MISC, "APSD=%s PD=%d QC3P5=%d\n", - apsd_result->name, chg->pd_active, chg->qc3p5_detected); + smblib_dbg(chg, PR_MOTO, "APSD=%s PD=%d\n", + apsd_result->name, chg->pd_active); return apsd_result; } @@ -1214,8 +1379,12 @@ static void smblib_uusb_removal(struct smb_charger *chg) /* reset both usbin current and voltage votes */ vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0); vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0); +#ifdef QCOM_BASE vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, is_flash_active(chg) ? SDP_CURRENT_UA : SDP_100_MA); +#else + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0); +#endif vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0); vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER, false, 0); vote(chg->usb_icl_votable, HVDCP2_12V_ICL_VOTER, false, 0); @@ -1234,14 +1403,24 @@ static void smblib_uusb_removal(struct smb_charger *chg) vote(chg->cp_disable_votable, SW_THERM_REGULATION_VOTER, false, 0); + /* reconfigure allowed voltage for HVDCP */ + rc = smblib_set_adapter_allowance(chg, + USBIN_ADAPTER_ALLOW_5V_TO_9V); + if (rc < 0) + smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_TO_9V rc=%d\n", + rc); + /* reset USBOV votes and cancel work */ cancel_delayed_work_sync(&chg->usbov_dbc_work); vote(chg->awake_votable, USBOV_DBC_VOTER, false, 0); chg->dbc_usbov = false; +#ifdef QCOM_BASE chg->voltage_min_uv = MICRO_5V; chg->voltage_max_uv = MICRO_5V; +#endif chg->usbin_forced_max_uv = 0; + chg->pd_contract_uv = 0; chg->usb_icl_delta_ua = 0; chg->pulse_cnt = 0; chg->uusb_apsd_rerun_done = false; @@ -1321,6 +1500,9 @@ int smblib_rerun_apsd_if_required(struct smb_charger *chg) union power_supply_propval val; int rc; + if (chg->mmi.factory_mode) + return 0; + rc = smblib_get_prop_usb_present(chg, &val); if (rc < 0) { smblib_err(chg, "Couldn't get usb present rc = %d\n", rc); @@ -1418,6 +1600,13 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) /* suspend if 25mA or less is requested */ bool suspend = (icl_ua <= USBIN_25MA); + if (chg->mmi.factory_mode) { + pr_err("USB ICL callback in Facory Mode! %d\n", icl_ua); + return rc; + } + + smblib_dbg(chg, PR_MISC, "%d uA\n", icl_ua); + if (chg->chg_param.smb_version == PMI632_SUBTYPE) schgm_flash_torch_priority(chg, suspend ? TORCH_BOOST_MODE : TORCH_BUCK_MODE); @@ -1435,8 +1624,7 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) /* configure current */ if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB - && (chg->typec_legacy - || chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT + && (chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT || chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)) { rc = set_sdp_current(chg, icl_ua); if (rc < 0) { @@ -1663,6 +1851,9 @@ static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data, if (chg->chg_param.smb_version == PMI632_SUBTYPE) return 0; + if (chg->mmi.factory_mode) + return 0; + /* resume input if suspend is invalid */ if (suspend < 0) suspend = 0; @@ -1670,6 +1861,39 @@ static int smblib_dc_suspend_vote_callback(struct votable *votable, void *data, return smblib_set_dc_suspend(chg, (bool)suspend); } +static int smblib_dc_icl_vote_callback(struct votable *votable, void *data, + int icl_ua, const char *client) +{ + struct smb_charger *chg = data; + int rc = 0; + bool suspend; + + if (icl_ua < 0) { + smblib_dbg(chg, PR_MISC, "No Voter hence suspending\n"); + icl_ua = 0; + } + + suspend = (icl_ua <= USBIN_25MA); + if (suspend) + goto suspend; + + rc = smblib_set_charge_param(chg, &chg->param.dc_icl, icl_ua); + if (rc < 0) { + smblib_err(chg, "Couldn't set DC input current limit rc=%d\n", + rc); + return rc; + } + +suspend: + rc = vote(chg->dc_suspend_votable, USER_VOTER, suspend, 0); + if (rc < 0) { + smblib_err(chg, "Couldn't vote to %s DC rc=%d\n", + suspend ? "suspend" : "resume", rc); + return rc; + } + return rc; +} + static int smblib_awake_vote_callback(struct votable *votable, void *data, int awake, const char *client) { @@ -1689,6 +1913,9 @@ static int smblib_chg_disable_vote_callback(struct votable *votable, void *data, struct smb_charger *chg = data; int rc; + if (chg->mmi.factory_mode) + return 0; + rc = smblib_masked_write(chg, CHARGING_ENABLE_CMD_REG, CHARGING_ENABLE_CMD_BIT, chg_disable ? 0 : CHARGING_ENABLE_CMD_BIT); @@ -1697,6 +1924,7 @@ static int smblib_chg_disable_vote_callback(struct votable *votable, void *data, chg_disable ? "disable" : "enable", rc); return rc; } + smblib_run_aicl(chg, RESTART_AICL); return 0; } @@ -1858,6 +2086,9 @@ int smblib_vconn_regulator_is_enabled(struct regulator_dev *rdev) * OTG REGULATOR * *****************/ +static void mmi_set_extbat_state(struct smb_charger *chip, + enum ebchg_state state, + bool force); int smblib_vbus_regulator_enable(struct regulator_dev *rdev) { struct smb_charger *chg = rdev_get_drvdata(rdev); @@ -1865,12 +2096,22 @@ int smblib_vbus_regulator_enable(struct regulator_dev *rdev) smblib_dbg(chg, PR_OTG, "enabling OTG\n"); + mutex_lock(&chg->otg_lock); + chg->mmi.is_otg_enable = 1; + mutex_unlock(&chg->otg_lock); + + if (chg->mmi.ebchg_state == EB_SRC) + mmi_set_extbat_state(chg, EB_OFF, false); + rc = smblib_masked_write(chg, DCDC_CMD_OTG_REG, OTG_EN_BIT, OTG_EN_BIT); if (rc < 0) { smblib_err(chg, "Couldn't enable OTG rc=%d\n", rc); return rc; } + if (chg->usb_psy) + power_supply_changed(chg->usb_psy); + return 0; } @@ -1887,6 +2128,19 @@ int smblib_vbus_regulator_disable(struct regulator_dev *rdev) return rc; } + mutex_lock(&chg->otg_lock); + chg->mmi.is_otg_enable = 0; + mutex_unlock(&chg->otg_lock); + + if (chg->mmi.ebchg_state != EB_DISCONN) { + cancel_delayed_work(&chg->mmi.heartbeat_work); + schedule_delayed_work(&chg->mmi.heartbeat_work, + msecs_to_jiffies(0)); + } + + if (chg->usb_psy) + power_supply_changed(chg->usb_psy); + return 0; } @@ -1965,7 +2219,38 @@ static bool is_charging_paused(struct smb_charger *chg) return val & CHARGING_PAUSE_CMD_BIT; } -#define CUTOFF_COUNT 3 +int smblib_get_prop_batt_age(struct smb_charger *chg, + union power_supply_propval *val) +{ + int age; + int rc = -EINVAL; + union power_supply_propval pval = {0, }; + + if (chg->bms_psy) { + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_CHARGE_FULL, &pval); + if (rc < 0) { + smblib_err(chg, "Fail: get charge full rc=%d\n", + rc); + return rc; + } + + age = pval.intval * 100; + + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, &pval); + if (rc < 0) { + smblib_err(chg, "Fail: get chrg full design rc=%d\n", + rc); + return rc; + } + + val->intval = age / pval.intval; + } + return rc; +} + +#define CUTOFF_COUNT 3 int smblib_get_prop_batt_status(struct smb_charger *chg, union power_supply_propval *val) { @@ -2077,8 +2362,10 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, switch (stat) { case TERMINATE_CHARGE: case INHIBIT_CHARGE: +#ifdef QCOM_BASE val->intval = POWER_SUPPLY_STATUS_FULL; break; +#endif default: val->intval = POWER_SUPPLY_STATUS_DISCHARGING; break; @@ -2095,8 +2382,10 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, break; case TERMINATE_CHARGE: case INHIBIT_CHARGE: +#ifdef QCOM_BASE val->intval = POWER_SUPPLY_STATUS_FULL; break; +#endif case DISABLE_CHARGE: case PAUSE_CHARGE: val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; @@ -2121,6 +2410,9 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, return 0; } + if (chg->mmi.pres_chrg_step == STEP_FULL) + val->intval = POWER_SUPPLY_STATUS_FULL; + if (val->intval != POWER_SUPPLY_STATUS_CHARGING) return 0; @@ -2146,6 +2438,32 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, return 0; } +bool smblib_charge_halted(struct smb_charger *chg) +{ + u8 stat; + int rc; + bool flag = false; + + rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", + rc); + return flag; + } + stat = stat & BATTERY_CHARGER_STATUS_MASK; + + switch (stat) { + case TERMINATE_CHARGE: + case INHIBIT_CHARGE: + flag = true; + break; + default: + break; + } + + return flag; +} + int smblib_get_prop_batt_charge_type(struct smb_charger *chg, union power_supply_propval *val) { @@ -2230,6 +2548,7 @@ int smblib_get_prop_batt_health(struct smb_charger *chg, else val->intval = POWER_SUPPLY_HEALTH_GOOD; + val->intval = chg->mmi.batt_health; done: return rc; } @@ -2329,6 +2648,11 @@ int smblib_get_prop_batt_charge_done(struct smb_charger *chg, stat = stat & BATTERY_CHARGER_STATUS_MASK; val->intval = (stat == TERMINATE_CHARGE); + + if ((chg->mmi.pres_chrg_step == STEP_FULL) || + (chg->mmi.pres_chrg_step == STEP_EB)) + val->intval = 1; + return 0; } @@ -2406,21 +2730,17 @@ int smblib_set_prop_system_temp_level(struct smb_charger *chg, if (chg->thermal_levels <= 0) return -EINVAL; - if (val->intval > chg->thermal_levels) - return -EINVAL; - - chg->system_temp_level = val->intval; - - if (chg->system_temp_level == chg->thermal_levels) - return vote(chg->chg_disable_votable, - THERMAL_DAEMON_VOTER, true, 0); + if (val->intval >= chg->thermal_levels) + chg->system_temp_level = chg->thermal_levels - 1; + else + chg->system_temp_level = val->intval; vote(chg->chg_disable_votable, THERMAL_DAEMON_VOTER, false, 0); if (chg->system_temp_level == 0) return vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, false, 0); vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, true, - chg->thermal_mitigation[chg->system_temp_level]); + chg->thermal_mitigation[chg->system_temp_level] * 1000); return 0; } @@ -2596,6 +2916,7 @@ static void smblib_hvdcp_adaptive_voltage_change(struct smb_charger *chg) int smblib_dp_dm(struct smb_charger *chg, int val) { +#ifdef QCOM_BASE int target_icl_ua, rc = 0; union power_supply_propval pval; u8 stat; @@ -2734,6 +3055,9 @@ int smblib_dp_dm(struct smb_charger *chg, int val) } return rc; +#else + return 0; +#endif } int smblib_disable_hw_jeita(struct smb_charger *chg, bool disable) @@ -3104,7 +3428,12 @@ int smblib_get_prop_dc_online(struct smb_charger *chg, int smblib_get_prop_dc_current_max(struct smb_charger *chg, union power_supply_propval *val) { +#ifdef QCOM_BASE return smblib_get_charge_param(chg, &chg->param.dc_icl, &val->intval); +#else + val->intval = get_effective_result_locked(chg->dc_icl_votable); + return 0; +#endif } int smblib_get_prop_dc_voltage_max(struct smb_charger *chg, @@ -3130,6 +3459,7 @@ int smblib_get_prop_dc_voltage_max(struct smb_charger *chg, return 0; } +#ifdef QCOM_BASE int smblib_get_prop_dc_voltage_now(struct smb_charger *chg, union power_supply_propval *val) { @@ -3152,7 +3482,7 @@ int smblib_get_prop_dc_voltage_now(struct smb_charger *chg, return rc; } - +#endif /******************* * DC PSY SETTERS * *******************/ @@ -3160,8 +3490,15 @@ int smblib_get_prop_dc_voltage_now(struct smb_charger *chg, int smblib_set_prop_dc_current_max(struct smb_charger *chg, const union power_supply_propval *val) { +#ifdef QCOM_BASE chg->dcin_icl_user_set = true; return smblib_set_charge_param(chg, &chg->param.dc_icl, val->intval); +#else + int rc = 0; + + rc = vote(chg->dc_icl_votable, USER_VOTER, true, val->intval); + return rc; +#endif } #define DCIN_AICL_RERUN_DELAY_MS 5000 @@ -3276,6 +3613,7 @@ int smblib_get_prop_usb_online(struct smb_charger *chg, union power_supply_propval *val) { int rc = 0; +#ifdef QCOM_BASE u8 stat; if (get_client_vote_locked(chg->usb_icl_votable, USER_VOTER) == 0) { @@ -3300,6 +3638,9 @@ int smblib_get_prop_usb_online(struct smb_charger *chg, val->intval = (stat & USE_USBIN_BIT) && (stat & VALID_INPUT_POWER_SOURCE_STS_BIT); +#else + rc = smblib_get_prop_usb_present(chg, val); +#endif return rc; } @@ -3312,13 +3653,12 @@ int smblib_get_usb_online(struct smb_charger *chg, if (!val->intval) goto exit; - 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_USB) || + (chg->pd_active && chg->pd && + (usbpd_get_current_dr(chg->pd) == 1))) + val->intval = 0; + else + val->intval = 1; if (chg->real_charger_type == POWER_SUPPLY_TYPE_UNKNOWN) val->intval = 0; @@ -3343,7 +3683,8 @@ int smblib_get_prop_usb_voltage_max_design(struct smb_charger *chg, case POWER_SUPPLY_TYPE_USB_HVDCP_3P5: case POWER_SUPPLY_TYPE_USB_HVDCP_3: case POWER_SUPPLY_TYPE_USB_PD: - if (chg->chg_param.smb_version == PMI632_SUBTYPE) + if ((chg->chg_param.smb_version == PMI632_SUBTYPE) || + (chg->chg_param.smb_version == PM6150_SUBTYPE)) val->intval = MICRO_9V; else val->intval = MICRO_12V; @@ -4224,6 +4565,7 @@ int smblib_get_prop_connector_health(struct smb_charger *chg) return POWER_SUPPLY_HEALTH_COOL; } +#ifdef QCOM_BASE static int get_rp_based_dcp_current(struct smb_charger *chg, int typec_mode) { int rp_ua; @@ -4241,6 +4583,7 @@ static int get_rp_based_dcp_current(struct smb_charger *chg, int typec_mode) return rp_ua; } +#endif /******************* * USB PSY SETTERS * @@ -4251,6 +4594,7 @@ int smblib_set_prop_pd_current_max(struct smb_charger *chg, { int rc, icl; +#ifdef QCOM_BASE if (chg->pd_active) { icl = get_client_vote(chg->usb_icl_votable, PD_VOTER); rc = vote(chg->usb_icl_votable, PD_VOTER, true, val->intval); @@ -4259,10 +4603,25 @@ int smblib_set_prop_pd_current_max(struct smb_charger *chg, } else { rc = -EPERM; } +#else + bool enable = true; + if (0 == val->intval) + enable = false; + + if (chg->pd_active) { + icl = get_client_vote(chg->usb_icl_votable, PD_VOTER); + rc = vote(chg->usb_icl_votable, PD_VOTER, enable, val->intval); + if (val->intval != icl) + power_supply_changed(chg->usb_psy); + } else { + rc = -EPERM; + } +#endif return rc; } +#ifdef QCOM_BASE static int smblib_handle_usb_current(struct smb_charger *chg, int usb_current) { @@ -4347,10 +4706,12 @@ static int smblib_handle_usb_current(struct smb_charger *chg, return 0; } +#endif int smblib_set_prop_sdp_current_max(struct smb_charger *chg, const union power_supply_propval *val) { +#ifdef QCOM_BASE union power_supply_propval pval; int rc = 0; @@ -4373,6 +4734,29 @@ int smblib_set_prop_sdp_current_max(struct smb_charger *chg, rc = vote(chg->usb_icl_votable, PD_SUSPEND_SUPPORTED_VOTER, false, 0); } +#else + int rc = 0; + bool enable = true; + const struct apsd_result *apsd_result = smblib_get_apsd_result(chg); + + if (0 >= val->intval) + enable = false; + + if (!chg->pd_active && + (smblib_get_prop_typec_mode(chg) == + POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) && + (apsd_result->pst != POWER_SUPPLY_TYPE_USB_FLOAT)) { + rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, + enable, val->intval); + } else if (chg->system_suspend_supported) { + if (val->intval <= USBIN_25MA) + rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, + enable, val->intval); + else + rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, + false, 0); + } +#endif return rc; } @@ -4525,6 +4909,17 @@ int smblib_set_prop_typec_power_role(struct smb_charger *chg, goto unlock; } + if ((power_role != EN_SNK_ONLY_BIT) + && (chg->source_current_ma < DEFAULT_SOURCE_CURRENT_MA)) { + /*advertise default current*/ + rc = smblib_masked_write(chg, TYPE_C_CURRSRC_CFG_REG, + TYPEC_SRC_RP_SEL_MASK, TYPEC_SRC_RP_STD); + if (rc < 0) { + smblib_err(chg, "Couldn't write to TYPE_C_CURRSRC_CFG_REG rc=%d\n", + rc); + } + } + rc = smblib_masked_write(chg, TYPE_C_MODE_CFG_REG, TYPEC_POWER_ROLE_CMD_MASK | TYPEC_TRY_MODE_MASK, power_role); @@ -4595,6 +4990,8 @@ int smblib_set_prop_pd_voltage_max(struct smb_charger *chg, max_uv = max(val->intval, chg->voltage_min_uv); if (chg->voltage_max_uv == max_uv) return 0; + if ((chg->pd_active) && (max_uv != chg->voltage_max_uv)) + chg->pd_contract_uv = 0; rc = smblib_set_usb_pd_fsw(chg, max_uv); if (rc < 0) { @@ -4620,8 +5017,9 @@ int smblib_set_prop_pd_voltage_max(struct smb_charger *chg, int smblib_set_prop_pd_active(struct smb_charger *chg, const union power_supply_propval *val) { +#ifdef QCOM_BASE const struct apsd_result *apsd = smblib_get_apsd_result(chg); - +#endif int rc = 0; int sec_charger, typec_mode; @@ -4636,9 +5034,23 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, smblib_apsd_enable(chg, !chg->pd_active); +#ifdef QCOM_BASE update_sw_icl_max(chg, apsd->pst); +#endif if (chg->pd_active) { +#ifndef QCOM_BASE + /* + * advertise 1.5A current to comply with the Type-C Specification. + * While an explicit USB PD contract is in place, the provider shall + * advertisea USB Type-C Current of either 1.5 A or 3.0 A.*/ + rc = smblib_masked_write(chg, TYPE_C_CURRSRC_CFG_REG, + TYPEC_SRC_RP_SEL_MASK, TYPEC_SRC_RP_1P5A); + if (rc < 0) { + smblib_err(chg, "Couldn't write to TYPE_C_CURRSRC_CFG_REG rc=%d\n", + rc); + } +#endif vote(chg->limited_irq_disable_votable, CHARGER_TYPE_VOTER, false, 0); vote(chg->hdc_irq_disable_votable, CHARGER_TYPE_VOTER, @@ -4653,6 +5065,12 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0); + if (chg->pd_contract_uv <= 0) { + cancel_delayed_work(&chg->pd_contract_work); + schedule_delayed_work(&chg->pd_contract_work, + msecs_to_jiffies(2000)); + } + /* * For PPS, Charge Pump is preferred over parallel charger if * present. @@ -4667,6 +5085,17 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, rc); } } else { +#ifndef QCOM_BASE + if (chg->source_current_ma < DEFAULT_SOURCE_CURRENT_MA) { + /*advertise default current*/ + rc = smblib_masked_write(chg, TYPE_C_CURRSRC_CFG_REG, + TYPEC_SRC_RP_SEL_MASK, TYPEC_SRC_RP_STD); + if (rc < 0) { + smblib_err(chg, "Couldn't write to TYPE_C_CURRSRC_CFG_REG rc=%d\n", + rc); + } + } +#endif vote(chg->usb_icl_votable, PD_VOTER, false, 0); vote(chg->limited_irq_disable_votable, CHARGER_TYPE_VOTER, true, 0); @@ -4690,8 +5119,10 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, } } +#ifdef QCOM_BASE smblib_usb_pd_adapter_allowance_override(chg, !!chg->pd_active ? FORCE_5V : FORCE_NULL); +#endif smblib_update_usb_type(chg); if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB && @@ -5121,6 +5552,28 @@ static void smblib_eval_chg_termination(struct smb_charger *chg, u8 batt_status) } } +irqreturn_t smblib_handle_usbin_collapse(int irq, void *data) +{ + struct smb_irq_data *irq_data = data; + struct smb_charger *chg = irq_data->parent_data; + int rc; + union power_supply_propval val = {0, }; + + rc = smblib_get_prop_usb_present(chg, &val); + if (rc < 0) { + smblib_err(chg, "Couldn't get usb present rc=%d\n", rc); + return IRQ_HANDLED; + } + + if (val.intval && !chg->reverse_boost) { + cancel_delayed_work(&chg->mmi.heartbeat_work); + schedule_delayed_work(&chg->mmi.heartbeat_work, + msecs_to_jiffies(100)); + } + smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); + return IRQ_HANDLED; +} + irqreturn_t chg_state_change_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; @@ -5504,6 +5957,7 @@ void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg) wdata = &data->storm_data; update_storm_count(wdata, WEAK_CHG_STORM_COUNT); + chg->reverse_boost = false; vote(chg->usb_icl_votable, BOOST_BACK_VOTER, false, 0); vote(chg->usb_icl_votable, WEAK_CHARGER_VOTER, @@ -5523,6 +5977,8 @@ void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg) } #define PL_DELAY_MS 30000 +static int factory_kill_disable; +module_param(factory_kill_disable, int, 0644); void smblib_usb_plugin_locked(struct smb_charger *chg) { int rc; @@ -5562,6 +6018,9 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) vote(chg->awake_votable, PL_DELAY_VOTER, true, 0); schedule_delayed_work(&chg->pl_enable_work, msecs_to_jiffies(PL_DELAY_MS)); + + if (chg->mmi.factory_mode) + chg->mmi.factory_kill_armed = true; } else { /* Disable SW Thermal Regulation */ rc = smblib_set_sw_thermal_regulation(chg, false); @@ -5619,6 +6078,12 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc); smblib_update_usb_type(chg); + + if (chg->mmi.factory_kill_armed && !factory_kill_disable) { + smblib_err(chg, "Factory kill power off\n"); + orderly_poweroff(true); + } else + chg->mmi.factory_kill_armed = false; } if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) @@ -5628,6 +6093,10 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) !vbus_rising, 0); power_supply_changed(chg->usb_psy); + __pm_stay_awake(&chg->mmi.smblib_mmi_hb_wake_source); + cancel_delayed_work(&chg->mmi.heartbeat_work); + schedule_delayed_work(&chg->mmi.heartbeat_work, + msecs_to_jiffies(0)); smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n", vbus_rising ? "attached" : "detached"); } @@ -5722,8 +6191,10 @@ static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg, CHARGER_TYPE_VOTER, false, 0); vote(chg->hdc_irq_disable_votable, CHARGER_TYPE_VOTER, false, 0); + #ifdef QCOM_BASE vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, hvdcp_ua); + #endif } else { /* A plain DCP, enforce DCP ICL if specified */ vote(chg->usb_icl_votable, DCP_VOTER, @@ -5743,6 +6214,7 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg, rising ? "rising" : "falling"); } +#ifdef QCOM_BASE static void update_sw_icl_max(struct smb_charger *chg, int pst) { int typec_mode; @@ -5811,6 +6283,7 @@ static void update_sw_icl_max(struct smb_charger *chg, int pst) break; } } +#endif static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) { @@ -5821,7 +6294,9 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) apsd_result = smblib_update_usb_type(chg); +#ifdef QCOM_BASE update_sw_icl_max(chg, apsd_result->pst); +#endif switch (apsd_result->bit) { case SDP_CHARGER_BIT: @@ -5837,6 +6312,8 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) break; } + chg->mmi.apsd_done = true; + smblib_dbg(chg, PR_INTERRUPT, "IRQ: apsd-done rising; %s detected\n", apsd_result->name); } @@ -5901,6 +6378,10 @@ irqreturn_t usb_source_change_irq_handler(int irq, void *data) } smblib_dbg(chg, PR_INTERRUPT, "APSD_STATUS = 0x%02x\n", stat); + vote(chg->awake_votable, HEARTBEAT_VOTER, true, true); + cancel_delayed_work(&chg->mmi.heartbeat_work); + schedule_delayed_work(&chg->mmi.heartbeat_work, + msecs_to_jiffies(0)); return IRQ_HANDLED; } @@ -6218,6 +6699,7 @@ static void typec_src_removal(struct smb_charger *chg) struct storm_watch *wdata; int sec_charger; u8 val[2] = {0}; + union power_supply_propval val; sec_charger = chg->sec_pl_present ? POWER_SUPPLY_CHARGER_SEC_PL : POWER_SUPPLY_CHARGER_SEC_NONE; @@ -6247,8 +6729,12 @@ static void typec_src_removal(struct smb_charger *chg) cancel_delayed_work_sync(&chg->pl_enable_work); /* reset input current limit voters */ +#ifdef QCOM_BASE vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, is_flash_active(chg) ? SDP_CURRENT_UA : SDP_100_MA); +#else + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0); +#endif vote(chg->usb_icl_votable, PD_VOTER, false, 0); vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); vote(chg->usb_icl_votable, DCP_VOTER, false, 0); @@ -6259,6 +6745,16 @@ static void typec_src_removal(struct smb_charger *chg) vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, false, 0); vote(chg->usb_icl_votable, THERMAL_THROTTLE_VOTER, false, 0); vote(chg->usb_icl_votable, LPD_VOTER, false, 0); + vote(chg->usb_icl_votable, ICL_LIMIT_VOTER, false, 0); + rc = smblib_get_prop_usb_present(chg, &val); + if (rc < 0) { + smblib_err(chg, "Error getting USB Present rc = %d\n", rc); + chg->mmi.apsd_done = false; + chg->mmi.charger_rate = POWER_SUPPLY_CHARGE_RATE_NONE; + } else if (!val.intval) { + chg->mmi.apsd_done = false; + chg->mmi.charger_rate = POWER_SUPPLY_CHARGE_RATE_NONE; + } /* reset usb irq voters */ vote(chg->limited_irq_disable_votable, CHARGER_TYPE_VOTER, @@ -6288,8 +6784,10 @@ static void typec_src_removal(struct smb_charger *chg) chg->pulse_cnt = 0; chg->usb_icl_delta_ua = 0; +#ifdef QCOM_BASE chg->voltage_min_uv = MICRO_5V; chg->voltage_max_uv = MICRO_5V; +#endif chg->usbin_forced_max_uv = 0; chg->chg_param.forced_main_fcc = 0; @@ -6301,7 +6799,7 @@ static void typec_src_removal(struct smb_charger *chg) vote_override(chg->usb_icl_votable, CC_MODE_VOTER, false, 0); vote(chg->cp_disable_votable, OVERHEAT_LIMIT_VOTER, false, 0); vote(chg->usb_icl_votable, OVERHEAT_LIMIT_VOTER, false, 0); - + chg->pd_contract_uv = 0; /* write back the default FLOAT charger configuration */ rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG, (u8)FLOAT_OPTIONS_MASK, chg->float_cfg); @@ -6321,6 +6819,13 @@ static void typec_src_removal(struct smb_charger *chg) pr_err("Couldn't clear SDAM ADC status rc=%d\n", rc); } + /* reconfigure allowed voltage for HVDCP */ + rc = smblib_set_adapter_allowance(chg, + USBIN_ADAPTER_ALLOW_5V_TO_9V); + if (rc < 0) + smblib_err(chg, "Couldn't set USBIN_ADAPTER_ALLOW_5V_TO_9V rc=%d\n", + rc); + if (!chg->pr_swap_in_progress) { rc = smblib_usb_pd_adapter_allowance_override(chg, FORCE_NULL); if (rc < 0) @@ -6360,15 +6865,24 @@ static void typec_src_removal(struct smb_charger *chg) chg->typec_legacy = false; + chg->mmi.charging_limit_modes = CHARGING_LIMIT_OFF; + chg->mmi.hvdcp3_con = false; + chg->mmi.vbus_inc_cnt = 0; + vote(chg->awake_votable, HEARTBEAT_VOTER, true, true); + cancel_delayed_work(&chg->mmi.heartbeat_work); + schedule_delayed_work(&chg->mmi.heartbeat_work, + msecs_to_jiffies(0)); del_timer_sync(&chg->apsd_timer); chg->apsd_ext_timeout = false; } static void typec_mode_unattached(struct smb_charger *chg) { +#ifdef QCOM_BASE vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, USBIN_100MA); +#endif } - +#ifdef QCOM_BASE static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode) { const struct apsd_result *apsd = smblib_get_apsd_result(chg); @@ -6393,6 +6907,7 @@ static void smblib_handle_rp_change(struct smb_charger *chg, int typec_mode) smblib_dbg(chg, PR_MISC, "CC change old_mode=%d new_mode=%d\n", chg->typec_mode, typec_mode); } +#endif static void smblib_lpd_launch_ra_open_work(struct smb_charger *chg) { @@ -6467,7 +6982,8 @@ 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; - int typec_mode; + int typec_mode, icl_vote_ma; + bool typec_chg = false; if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) { smblib_dbg(chg, PR_INTERRUPT, @@ -6478,9 +6994,31 @@ irqreturn_t typec_state_change_irq_handler(int irq, void *data) typec_mode = smblib_get_prop_typec_mode(chg); if (chg->sink_src_mode != UNATTACHED_MODE && (typec_mode != chg->typec_mode)) +#ifdef QCOM_BASE smblib_handle_rp_change(chg, typec_mode); +#else + typec_chg = true; +#endif + chg->typec_mode = typec_mode; + if (typec_chg) { + icl_vote_ma = get_client_vote(chg->usb_icl_votable, + HEARTBEAT_VOTER) / 1000; + if ((typec_mode == POWER_SUPPLY_TYPEC_SOURCE_MEDIUM) && + (icl_vote_ma > 1500)) + vote(chg->usb_icl_votable, HEARTBEAT_VOTER, + true, 1500000); + else if ((typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) && + (icl_vote_ma > 500)) + vote(chg->usb_icl_votable, HEARTBEAT_VOTER, + true, 500000); + vote(chg->awake_votable, HEARTBEAT_VOTER, true, true); + cancel_delayed_work(&chg->mmi.heartbeat_work); + schedule_delayed_work(&chg->mmi.heartbeat_work, + msecs_to_jiffies(0)); + } + smblib_dbg(chg, PR_INTERRUPT, "IRQ: cc-state-change; Type-C %s detected\n", smblib_typec_mode_name[chg->typec_mode]); @@ -6725,7 +7263,7 @@ static enum alarmtimer_restart dcin_aicl_alarm_cb(struct alarm *alarm, return ALARMTIMER_NORESTART; } - +#ifdef QCOM_BASE static void dcin_icl_decrement(struct smb_charger *chg) { int rc, icl; @@ -6778,6 +7316,7 @@ irqreturn_t dcin_uv_irq_handler(int irq, void *data) return IRQ_HANDLED; } +#endif irqreturn_t dc_plugin_irq_handler(int irq, void *data) { @@ -6940,8 +7479,9 @@ irqreturn_t switcher_power_ok_irq_handler(int irq, void *data) struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; struct storm_watch *wdata = &irq_data->storm_data; - int rc, usb_icl; - u8 stat; + int pok_irq = chg->irq_info[SWITCHER_POWER_OK_IRQ].irq; + int rc; + u8 stat, susp; if (!(chg->wa_flags & BOOST_BACK_WA)) return IRQ_HANDLED; @@ -6952,15 +7492,35 @@ irqreturn_t switcher_power_ok_irq_handler(int irq, void *data) return IRQ_HANDLED; } + rc = smblib_read(chg, USBIN_CMD_IL_REG, &susp); + if (rc < 0) { + smblib_err(chg, "Couldn't read USBIN_CMD_IL rc=%d\n", rc); + return IRQ_HANDLED; + } + /* skip suspending input if its already suspended by some other voter */ - usb_icl = get_effective_result(chg->usb_icl_votable); - if ((stat & USE_USBIN_BIT) && usb_icl >= 0 && usb_icl <= USBIN_25MA) + if ((stat & USE_USBIN_BIT) && (susp & USBIN_SUSPEND_BIT)) return IRQ_HANDLED; if (stat & USE_DCIN_BIT) return IRQ_HANDLED; if (is_storming(&irq_data->storm_data)) { + chg->reverse_boost = true; + stat = 0; + rc = smblib_read(chg, TYPE_C_SNK_STATUS_REG, &stat); + stat = stat & DETECTED_SRC_TYPE_MASK; + if (rc < 0) { + smblib_err(chg, + "Error getting USB Pres rc = %d\n", rc); + } else if (stat) { + smblib_err(chg, "USB Present, Disable Power OK IRQ\n"); + disable_irq_nosync(pok_irq); + cancel_delayed_work(&chg->mmi.heartbeat_work); + schedule_delayed_work(&chg->mmi.heartbeat_work, + msecs_to_jiffies(100)); + return IRQ_HANDLED; + } /* This could be a weak charger reduce ICL */ if (!is_client_vote_enabled(chg->usb_icl_votable, WEAK_CHARGER_VOTER)) { @@ -7233,6 +7793,55 @@ out: vote(chg->awake_votable, DETACH_DETECT_VOTER, false, 0); } + #define MAX_INPUT_PWR_UW 18000000 +static void smblib_pd_contract_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + pd_contract_work.work); + int rc, max_ua; + + if (!chg->pd) { + chg->pd = devm_usbpd_get_by_phandle(chg->dev, + "qcom,usbpd-phandle"); + if (IS_ERR_OR_NULL(chg->pd)) { + pr_err("Error getting the pd phandle %ld\n", + PTR_ERR(chg->pd)); + chg->pd = NULL; + } + } + if (!chg->pd || !chg->pd_active) + return; + + chg->pd_contract_uv = usbpd_select_pdo_match(chg->pd); + + if (chg->pd_contract_uv == -ENOTSUPP) + return; + + if (chg->pd_contract_uv <= 0) { + schedule_delayed_work(&chg->pd_contract_work, + msecs_to_jiffies(500)); + return; + } else + power_supply_changed(chg->usb_psy); + + if (chg->pd_contract_uv >= MICRO_9V) + smblib_set_opt_switcher_freq(chg, chg->chg_freq.freq_9V); + else + smblib_set_opt_switcher_freq(chg, chg->chg_freq.freq_5V); + + max_ua = (MAX_INPUT_PWR_UW / (chg->pd_contract_uv / 1000)) * 1000; + smblib_dbg(chg, PR_MISC, "smblib_pd_contract_work: %d uV, %d uA\n", + chg->pd_contract_uv, max_ua); + + rc = vote(chg->usb_icl_votable, ICL_LIMIT_VOTER, true, max_ua); + if (rc < 0) + smblib_err(chg, "Error setting %d uA rc=%d\n", max_ua, rc); + + cancel_delayed_work(&chg->mmi.heartbeat_work); + schedule_delayed_work(&chg->mmi.heartbeat_work, 0); + +} + static void smblib_uusb_otg_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, @@ -8009,6 +8618,14 @@ static int smblib_create_votables(struct smb_charger *chg) return rc; } + chg->dc_icl_votable = create_votable("DC_ICL", VOTE_MIN, + smblib_dc_icl_vote_callback, + chg); + if (IS_ERR(chg->dc_icl_votable)) { + rc = PTR_ERR(chg->dc_icl_votable); + return rc; + } + chg->awake_votable = create_votable("AWAKE", VOTE_SET_ANY, smblib_awake_vote_callback, chg); @@ -8074,6 +8691,8 @@ static void smblib_destroy_votables(struct smb_charger *chg) { if (chg->dc_suspend_votable) destroy_votable(chg->dc_suspend_votable); + if (chg->dc_icl_votable) + destroy_votable(chg->dc_icl_votable); if (chg->usb_icl_votable) destroy_votable(chg->usb_icl_votable); if (chg->awake_votable) @@ -8102,6 +8721,8 @@ static void smblib_iio_deinit(struct smb_charger *chg) iio_channel_release(chg->iio.skin_temp_chan); if (!IS_ERR_OR_NULL(chg->iio.smb_temp_chan)) iio_channel_release(chg->iio.smb_temp_chan); + if (!IS_ERR_OR_NULL(chg->iio.dcin_v_chan)) + iio_channel_release(chg->iio.dcin_v_chan); } int smblib_init(struct smb_charger *chg) @@ -8109,11 +8730,13 @@ int smblib_init(struct smb_charger *chg) union power_supply_propval prop_val; int rc = 0; + device_init_wakeup(chg->dev, true); mutex_init(&chg->smb_lock); mutex_init(&chg->irq_status_lock); mutex_init(&chg->dcin_aicl_lock); mutex_init(&chg->dpdm_lock); spin_lock_init(&chg->typec_pr_lock); + mutex_init(&chg->otg_lock); INIT_WORK(&chg->bms_update_work, bms_update_work); INIT_WORK(&chg->pl_update_work, pl_update_work); INIT_WORK(&chg->jeita_update_work, jeita_update_work); @@ -8122,6 +8745,7 @@ int smblib_init(struct smb_charger *chg) INIT_DELAYED_WORK(&chg->clear_hdc_work, clear_hdc_work); INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work); INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work); + INIT_DELAYED_WORK(&chg->pd_contract_work, smblib_pd_contract_work); INIT_DELAYED_WORK(&chg->uusb_otg_work, smblib_uusb_otg_work); INIT_DELAYED_WORK(&chg->bb_removal_work, smblib_bb_removal_work); INIT_DELAYED_WORK(&chg->lpd_ra_open_work, smblib_lpd_ra_open_work); @@ -8260,11 +8884,15 @@ int smblib_init(struct smb_charger *chg) return -EINVAL; } + mmi_init(chg); + return rc; } int smblib_deinit(struct smb_charger *chg) { + mmi_deinit(chg); + switch (chg->mode) { case PARALLEL_MASTER: if (chg->uusb_moisture_protection_enabled) { @@ -8308,3 +8936,2912 @@ int smblib_deinit(struct smb_charger *chg) return 0; } + +/********************* + * MMI Functionality * + *********************/ + +struct smb_charger *mmi_chip; + +static char *stepchg_str[] = { + [STEP_MAX] = "MAX", + [STEP_NORM] = "NORMAL", + [STEP_EB] = "EXTERNAL BATT", + [STEP_FULL] = "FULL", + [STEP_FLOAT] = "FLOAT", + [STEP_DEMO] = "DEMO", + [STEP_STOP] = "STOP", + [STEP_NONE] = "NONE", +}; + +static char *ebchg_str[] = { + [EB_DISCONN] = "DISCONN", + [EB_SINK] = "SINK", + [EB_SRC] = "SOURCE", + [EB_OFF] = "OFF", +}; + +int smblib_get_prop_dc_voltage_now(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc = 0; + + rc = smblib_get_prop_dc_present(chg, val); + if (rc < 0 || !val->intval) + return rc; + + if (!chg->iio.dcin_v_chan || + PTR_ERR(chg->iio.dcin_v_chan) == -EPROBE_DEFER) + chg->iio.dcin_v_chan = iio_channel_get(chg->dev, "dcin_v"); + + if (IS_ERR(chg->iio.dcin_v_chan)) + return PTR_ERR(chg->iio.dcin_v_chan); + + return iio_read_channel_processed(chg->iio.dcin_v_chan, &val->intval); +} + +#define MIN_TEMP_C -20 +#define MAX_TEMP_C 60 +#define MIN_MAX_TEMP_C 47 +#define HYSTERISIS_DEGC 2 +static bool mmi_find_temp_zone(struct smb_charger *chip, int temp_c) +{ + int prev_zone, num_zones; + struct mmi_temp_zone *zones; + int hotter_t, hotter_fcc; + int colder_t, colder_fcc; + int i; + int max_temp; + + if (!chip) { + smblib_dbg(chip, PR_MOTO, "called before chip valid!\n"); + return false; + } + + zones = chip->mmi.temp_zones; + num_zones = chip->mmi.num_temp_zones; + prev_zone = chip->mmi.pres_temp_zone; + + if (chip->mmi.max_chrg_temp >= MIN_MAX_TEMP_C) + max_temp = chip->mmi.max_chrg_temp; + else + max_temp = zones[num_zones - 1].temp_c; + + if (prev_zone == ZONE_NONE) { + for (i = num_zones - 1; i >= 0; i--) { + if (temp_c >= zones[i].temp_c) { + if (i == num_zones - 1) + chip->mmi.pres_temp_zone = ZONE_HOT; + else + chip->mmi.pres_temp_zone = i + 1; + return true; + } + } + chip->mmi.pres_temp_zone = ZONE_COLD; + return true; + } + + if (prev_zone == ZONE_COLD) { + if (temp_c >= MIN_TEMP_C + HYSTERISIS_DEGC) + chip->mmi.pres_temp_zone = ZONE_FIRST; + } else if (prev_zone == ZONE_HOT) { + if (temp_c <= max_temp - HYSTERISIS_DEGC) + chip->mmi.pres_temp_zone = num_zones - 1; + } else { + if (prev_zone == ZONE_FIRST) { + hotter_fcc = zones[prev_zone + 1].fcc_max_ma; + colder_fcc = 0; + hotter_t = zones[prev_zone].temp_c; + colder_t = MIN_TEMP_C; + } else if (prev_zone == num_zones - 1) { + hotter_fcc = 0; + colder_fcc = zones[prev_zone - 1].fcc_max_ma; + hotter_t = zones[prev_zone].temp_c; + colder_t = zones[prev_zone - 1].temp_c; + } else { + hotter_fcc = zones[prev_zone + 1].fcc_max_ma; + colder_fcc = zones[prev_zone - 1].fcc_max_ma; + hotter_t = zones[prev_zone].temp_c; + colder_t = zones[prev_zone - 1].temp_c; + } + + if (zones[prev_zone].fcc_max_ma < hotter_fcc) + hotter_t += HYSTERISIS_DEGC; + + if (zones[prev_zone].fcc_max_ma < colder_fcc) + colder_t -= HYSTERISIS_DEGC; + + if (temp_c < MIN_TEMP_C) + chip->mmi.pres_temp_zone = ZONE_COLD; + else if (temp_c >= max_temp) + chip->mmi.pres_temp_zone = ZONE_HOT; + else if (temp_c >= hotter_t) + chip->mmi.pres_temp_zone++; + else if (temp_c < colder_t) + chip->mmi.pres_temp_zone--; + } + + if (prev_zone != chip->mmi.pres_temp_zone) { + smblib_dbg(chip, PR_MOTO, + "Entered Temp Zone %d!\n", + chip->mmi.pres_temp_zone); + return true; + } + + return false; +} + +#define TAPER_COUNT 2 +#define TAPER_DROP_MA 100 +static bool mmi_has_current_tapered(struct smb_charger *chip, + int batt_ma, int taper_ma) +{ + bool change_state = false; + int allowed_fcc, target_ma; + + if (!chip) { + smblib_dbg(chip, PR_MOTO, "called before chip valid!\n"); + return false; + } + + allowed_fcc = get_effective_result(chip->fcc_votable) / 1000; + + if (allowed_fcc >= taper_ma) + target_ma = taper_ma; + else + target_ma = allowed_fcc - TAPER_DROP_MA; + + if (batt_ma < 0) { + batt_ma *= -1; + if (batt_ma <= target_ma) + if (chip->mmi.chrg_taper_cnt >= TAPER_COUNT) { + change_state = true; + chip->mmi.chrg_taper_cnt = 0; + } else + chip->mmi.chrg_taper_cnt++; + else + chip->mmi.chrg_taper_cnt = 0; + } else { + if (chip->mmi.chrg_taper_cnt >= TAPER_COUNT) { + change_state = true; + chip->mmi.chrg_taper_cnt = 0; + } else + chip->mmi.chrg_taper_cnt++; + } + + return change_state; +} + +static bool is_wls_present(struct smb_charger *chip) +{ + int rc; + union power_supply_propval ret = {0, }; + const struct power_supply_desc *wls; + + if (chip->mmi.wls_psy && chip->mmi.wls_psy->desc) + wls = chip->mmi.wls_psy->desc; + else + return false; + + if (!wls->get_property) + return false; + + rc = wls->get_property(chip->mmi.wls_psy, + POWER_SUPPLY_PROP_PRESENT, + &ret); + if (rc < 0) { + smblib_err(chip, "Couldn't get wls status\n"); + return false; + } + + return ret.intval ? true : false; +} + +static bool is_usbeb_present(struct smb_charger *chip) +{ + int rc; + union power_supply_propval ret = {0, }; + const struct power_supply_desc *usbeb; + + if (chip->mmi.usbeb_psy && chip->mmi.usbeb_psy->desc) + usbeb = chip->mmi.usbeb_psy->desc; + else + return false; + + if (!usbeb->get_property) + return false; + + rc = power_supply_get_property(chip->mmi.usbeb_psy, + POWER_SUPPLY_PROP_PRESENT, + &ret); + if (rc < 0) { + smblib_err(chip, "Couldn't get usbeb status\n"); + return false; + } + + return ret.intval ? true : false; +} + +static int get_eb_pwr_prop(struct smb_charger *chip, + enum power_supply_property prop) +{ + union power_supply_propval ret = {0, }; + int eb_prop; + int rc; + struct power_supply *eb_pwr_psy; + + eb_pwr_psy = + power_supply_get_by_name((char *)chip->mmi.eb_pwr_psy_name); + if (!eb_pwr_psy || !eb_pwr_psy->desc || + !eb_pwr_psy->desc->get_property) + return -ENODEV; + + rc = power_supply_get_property(eb_pwr_psy, prop, &ret); + if (rc) { + smblib_dbg(chip, PR_MISC, + "eb pwr error reading Prop %d rc = %d\n", prop, rc); + ret.intval = -EINVAL; + } + eb_prop = ret.intval; + + power_supply_put(eb_pwr_psy); + + return eb_prop; +} + +static int is_eb_present(struct smb_charger *chip) +{ + int ret; + struct power_supply *eb_batt_psy; + + eb_batt_psy = + power_supply_get_by_name((char *)chip->mmi.eb_batt_psy_name); + if (!eb_batt_psy || !eb_batt_psy->desc || + !eb_batt_psy->desc->get_property) { + ret = false; + } else { + ret = true; + power_supply_put(eb_batt_psy); + } + + pr_info("%s eb is present? %d\n", __func__, ret); + + return ret; +} + +static int get_eb_prop(struct smb_charger *chip, + enum power_supply_property prop) +{ + union power_supply_propval ret = {0, }; + int eb_prop; + int rc; + struct power_supply *eb_batt_psy; + + eb_batt_psy = + power_supply_get_by_name((char *)chip->mmi.eb_batt_psy_name); + if (!eb_batt_psy || !eb_batt_psy->desc || + !eb_batt_psy->desc->get_property) + return -ENODEV; + + rc = power_supply_get_property(eb_batt_psy, prop, &ret); + if (rc) { + smblib_dbg(chip, PR_MISC, + "eb batt error reading Prop %d rc = %d\n", + prop, rc); + ret.intval = -EINVAL; + } + eb_prop = ret.intval; + + power_supply_put(eb_batt_psy); + + return eb_prop; +} + +irqreturn_t dcin_uv_irq_handler(int irq, void *data) +{ + struct smb_irq_data *irq_data = data; + struct smb_charger *chg = irq_data->parent_data; + int ret, dcin_chg_cur = 0; + union power_supply_propval vol = {0, }; + + smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); + + smblib_get_prop_dc_voltage_now(chg, + &vol); + smblib_dbg(chg, PR_INTERRUPT, "dcin: %d\n", vol); + if ((vol.intval > 3800000) && (vol.intval < 4600000)) + ret = 1; + else + ret = 0; + + if (ret <= 0) { + goto end; + } + + dcin_chg_cur = get_effective_result(chg->dc_icl_votable); + + //divide dcin current by uv abnormal count nubmer + dcin_chg_cur = dcin_chg_cur - (dcin_chg_cur / 4); + if (dcin_chg_cur <= USBIN_25MA) + dcin_chg_cur = USBIN_100MA; + + chg->mmi.dc_ebmax_current_ma = dcin_chg_cur / 1000; + chg->mmi.cl_ebsrc = chg->mmi.dc_ebmax_current_ma; + vote(chg->dc_icl_votable, + HEARTBEAT_VOTER, true, dcin_chg_cur); + + pr_info("DCIN uv set dcin %d\n", dcin_chg_cur); + +end: + return IRQ_HANDLED; +} + +static void mmi_check_extbat_ability(struct smb_charger *chip, char *able) +{ + int rc; + const struct power_supply_desc *eb_pwr_d; + union power_supply_propval ret = {0, }; + struct power_supply *eb_pwr_psy; + + if (!able) + return; + + eb_pwr_psy = + power_supply_get_by_name((char *)chip->mmi.eb_pwr_psy_name); + + *able = 0; + + if (!eb_pwr_psy || !eb_pwr_psy->desc) { + *able |= (EB_RCV_NEVER | EB_SND_NEVER); + return; + } + + eb_pwr_d = eb_pwr_psy->desc; + rc = eb_pwr_d->get_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_INTERNAL_RECEIVE, + &ret); + if (rc) { + smblib_err(chip, + "Could not read Receive Params rc = %d\n", rc); + *able |= EB_RCV_NEVER; + } else if (ret.intval == POWER_SUPPLY_PTP_INT_RCV_PARALLEL) + *able |= EB_RCV_PARALLEL; + else if ((ret.intval == POWER_SUPPLY_PTP_INT_RCV_NEVER) || + (ret.intval == POWER_SUPPLY_PTP_INT_RCV_UNKNOWN)) + *able |= EB_RCV_NEVER; + + rc = eb_pwr_d->get_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_POWER_REQUIRED, + &ret); + if (rc) { + smblib_err(chip, + "Could not read Power Required rc = %d\n", rc); + *able |= EB_RCV_NEVER; + } else if ((ret.intval == POWER_SUPPLY_PTP_POWER_NOT_REQUIRED) || + (ret.intval == POWER_SUPPLY_PTP_POWER_REQUIREMENTS_UNKNOWN)) + *able |= EB_RCV_NEVER; + + rc = eb_pwr_d->get_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_POWER_AVAILABLE, + &ret); + if (rc) { + smblib_err(chip, + "Could not read Power Available rc = %d\n", rc); + *able |= EB_SND_NEVER; + } else if ((ret.intval == POWER_SUPPLY_PTP_POWER_NOT_AVAILABLE) || + (ret.intval == POWER_SUPPLY_PTP_POWER_AVAILABILITY_UNKNOWN)) + *able |= EB_SND_NEVER; + else if (ret.intval == POWER_SUPPLY_PTP_POWER_AVAILABLE_INTERNAL) { + rc = eb_pwr_d->get_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_INTERNAL_SEND, + &ret); + if (rc) { + smblib_err(chip, + "Could not read Send Params rc = %d\n", rc); + *able |= EB_SND_NEVER; + } else if ((ret.intval == POWER_SUPPLY_PTP_INT_SND_NEVER) || + (ret.intval == POWER_SUPPLY_PTP_INT_SND_UNKNOWN)) + *able |= EB_SND_NEVER; + else if (ret.intval == POWER_SUPPLY_PTP_INT_SND_LOW_BATT_SAVER) + *able |= EB_SND_LOW; + } else if (ret.intval == POWER_SUPPLY_PTP_POWER_AVAILABLE_EXTERNAL) + *able |= EB_SND_EXT; + + power_supply_put(eb_pwr_psy); +} + +static void mmi_set_extbat_in_cl(struct smb_charger *chip) +{ + int rc; + union power_supply_propval ret = {0, }; + struct power_supply *eb_pwr_psy = + power_supply_get_by_name((char *)chip->mmi.eb_pwr_psy_name); + + if (!eb_pwr_psy || !eb_pwr_psy->desc || + !eb_pwr_psy->desc->set_property) + return; + + ret.intval = chip->mmi.cl_ebchg * 1000; + + smblib_dbg(chip, PR_MISC, "Set EB In Current %d uA\n", ret.intval); + rc = power_supply_set_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_MAX_INPUT_CURRENT, + &ret); + if (rc) + smblib_err(chip, "Failed to set EB Input Current %d mA", + ret.intval / 1000); + + power_supply_put(eb_pwr_psy); +} + +static void mmi_get_extbat_out_cl(struct smb_charger *chip) +{ + int rc; + union power_supply_propval ret = {0, }; + struct power_supply *eb_pwr_psy = + power_supply_get_by_name((char *)chip->mmi.eb_pwr_psy_name); + int prev_cl_ebsrc = chip->mmi.cl_ebsrc; + bool ebsrc_max, wls_src_max; + + if (!eb_pwr_psy || !eb_pwr_psy->desc || + !eb_pwr_psy->desc->get_property) { + chip->mmi.cl_ebsrc = 0; + return; + } + + if (chip->mmi.turbo_pwr_ebsrc == TURBO_EBSRC_UNKNOWN) { + rc = power_supply_get_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_POWER_SOURCE, + &ret); + + switch (ret.intval) { + case POWER_SUPPLY_PTP_POWER_SOURCE_NONE_TURBO: + case POWER_SUPPLY_PTP_POWER_SOURCE_BATTERY_TURBO: + case POWER_SUPPLY_PTP_POWER_SOURCE_WIRED_TURBO: + case POWER_SUPPLY_PTP_POWER_SOURCE_WIRELESS_TURBO: + chip->mmi.turbo_pwr_ebsrc = TURBO_EBSRC_VALID; + break; + default: + chip->mmi.turbo_pwr_ebsrc = TURBO_EBSRC_NOT_SUPPORTED; + break; + } + } + + rc = power_supply_get_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_MAX_OUTPUT_CURRENT, + &ret); + if (rc) + smblib_err(chip, "Failed to get EB Output Current"); + else { + smblib_dbg(chip, PR_MISC, + "Get EB Out Current %d uA\n", ret.intval); + ret.intval /= 1000; + + wls_src_max = (is_wls_present(chip) && + (chip->mmi.vo_ebsrc > (MICRO_5V / 1000))); + ebsrc_max = + ((chip->mmi.turbo_pwr_ebsrc == TURBO_EBSRC_VALID) && + ((eb_attach_stop_soc == 100) || + is_wls_present(chip))); + + if (ret.intval == 0) + chip->mmi.cl_ebsrc = 0; + else if ((ret.intval < chip->mmi.dc_ebmax_current_ma) || + ebsrc_max || + wls_src_max || + is_usbeb_present(chip)) + chip->mmi.cl_ebsrc = ret.intval; + else + chip->mmi.cl_ebsrc = chip->mmi.dc_ebmax_current_ma; + } + + if (prev_cl_ebsrc != chip->mmi.cl_ebsrc) + smblib_dbg(chip, PR_MISC, "cl_ebsrc %d mA, retval %d mA\n", + chip->mmi.cl_ebsrc, ret.intval); + + power_supply_put(eb_pwr_psy); +} + +static void mmi_get_extbat_in_vl(struct smb_charger *chip) +{ + int rc; + union power_supply_propval ret = {0, }; + struct power_supply *eb_pwr_psy = + power_supply_get_by_name((char *)chip->mmi.eb_pwr_psy_name); + int prev_vi_ebsrc = chip->mmi.vi_ebsrc; + + if (!eb_pwr_psy || !eb_pwr_psy->desc || + !eb_pwr_psy->desc->get_property) { + chip->mmi.vi_ebsrc = 0; + return; + } + + rc = power_supply_get_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_MAX_INPUT_VOLTAGE, + &ret); + if (rc) { + smblib_err(chip, "Failed to get EB Input Voltage"); + chip->mmi.vi_ebsrc = MICRO_5V; + } else { + smblib_dbg(chip, PR_MISC, + "Get EB In Voltage %d uV\n", ret.intval); + chip->mmi.vi_ebsrc = ret.intval; + } + + if (prev_vi_ebsrc != chip->mmi.vi_ebsrc) + smblib_dbg(chip, PR_MISC, "vi_ebsrc %d uV, retval %d uV\n", + chip->mmi.vi_ebsrc, ret.intval); + + power_supply_put(eb_pwr_psy); +} + +#define USBIN_TOLER 500000 +static void mmi_set_extbat_in_volt(struct smb_charger *chip) +{ + int rc; + union power_supply_propval ret = {0, }; + struct power_supply *eb_pwr_psy = + power_supply_get_by_name((char *)chip->mmi.eb_pwr_psy_name); + + if (!eb_pwr_psy || !eb_pwr_psy->desc || + !eb_pwr_psy->desc->set_property) + return; + + rc = smblib_get_prop_usb_voltage_now(chip, &ret); + if (rc < 0) + ret.intval = MICRO_5V; + else if (ret.intval >= (MICRO_9V - USBIN_TOLER)) + ret.intval = MICRO_9V; + else + ret.intval = MICRO_5V; + + smblib_dbg(chip, PR_MISC, "Set EB In Voltage %d uV\n", ret.intval); + rc = power_supply_set_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_INPUT_VOLTAGE, + &ret); + if (rc) + smblib_err(chip, "Failed to set EB Input Voltage %d mV", + ret.intval / 1000); + + power_supply_put(eb_pwr_psy); +} + +static void mmi_get_extbat_out_volt(struct smb_charger *chip) +{ + int rc; + union power_supply_propval ret = {0, }; + struct power_supply *eb_pwr_psy = + power_supply_get_by_name((char *)chip->mmi.eb_pwr_psy_name); + int prev_vo_ebsrc = chip->mmi.vo_ebsrc; + + if (!eb_pwr_psy || !eb_pwr_psy->desc || + !eb_pwr_psy->desc->get_property) { + chip->mmi.vo_ebsrc = 0; + return; + } + + rc = power_supply_get_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_OUTPUT_VOLTAGE, + &ret); + if (rc) + smblib_err(chip, "Failed to get EB Output Voltage"); + else { + smblib_dbg(chip, PR_MISC, "Get EB Out Voltage %d uV\n", + ret.intval); + ret.intval /= 1000; + chip->mmi.vo_ebsrc = ret.intval; + } + + if (prev_vo_ebsrc != chip->mmi.vo_ebsrc) + smblib_dbg(chip, PR_MISC, "vo_ebsrc %d mV, retval %d mV\n", + chip->mmi.vo_ebsrc, ret.intval); + + power_supply_put(eb_pwr_psy); +} + +static void mmi_set_extbat_out_vl(struct smb_charger *chip) +{ + int rc; + union power_supply_propval ret = {0, }; + struct power_supply *eb_pwr_psy = + power_supply_get_by_name((char *)chip->mmi.eb_pwr_psy_name); + + if (!eb_pwr_psy || !eb_pwr_psy->desc || + !eb_pwr_psy->desc->set_property) + return; + + ret.intval = chip->mmi.vl_ebsrc; + + smblib_dbg(chip, PR_MISC, "Set EB Out Voltage %d uV\n", ret.intval); + rc = power_supply_set_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_MAX_OUTPUT_VOLTAGE, + &ret); + if (rc) + smblib_err(chip, "Failed to set EB Output Voltage %d mV", + ret.intval / 1000); + + power_supply_put(eb_pwr_psy); +} + +static int mmi_get_extbat_state(struct smb_charger *chip, int *state) +{ + int rc; + union power_supply_propval ret = {0, }; + struct power_supply *eb_pwr_psy = + power_supply_get_by_name((char *)chip->mmi.eb_pwr_psy_name); + + if (!eb_pwr_psy || !eb_pwr_psy->desc || + !eb_pwr_psy->desc->get_property) { + *state = EB_DISCONN; + chip->mmi.turbo_pwr_ebsrc = TURBO_EBSRC_UNKNOWN; + return 0; + } + + rc = power_supply_get_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_CURRENT_FLOW, + &ret); + power_supply_put(eb_pwr_psy); + + if (rc) { + smblib_dbg(chip, PR_MISC, "Failed to get EB State"); + return -EINVAL; + } + + smblib_dbg(chip, PR_MISC, "Get EB State %d\n", ret.intval); + switch (ret.intval) { + case POWER_SUPPLY_PTP_CURRENT_FROM_PHONE: + *state = EB_SINK; + break; + case POWER_SUPPLY_PTP_CURRENT_TO_PHONE: + *state = EB_SRC; + break; + case POWER_SUPPLY_PTP_CURRENT_OFF: + default: + *state = EB_OFF; + break; + } + + return 0; +} + +#define MAX_PINCTRL_TRIES 5 +static void mmi_set_extbat_state(struct smb_charger *chip, + enum ebchg_state state, + bool force) +{ + int rc = 0; + char ability = 0; + int i = 0; + int dcin_mv = 0; + int dcin_high_cnt = 0; + static unsigned char mmi_pinctrl_tries; + union power_supply_propval ret = {0, }; + struct power_supply *eb_pwr_psy = + power_supply_get_by_name((char *)chip->mmi.eb_pwr_psy_name); + + if (mmi_pinctrl_tries < MAX_PINCTRL_TRIES) { + chip->mmi.smb_pinctrl = + pinctrl_get_select(chip->dev, "eb_active"); + if (!chip->mmi.smb_pinctrl) + smblib_err(chip, + "Could not get/set pinctrl state active\n"); + chip->mmi.smb_pinctrl = pinctrl_get_select_default(chip->dev); + if (!chip->mmi.smb_pinctrl) + smblib_err(chip, "Could not get/set pinctrl state\n"); + + mmi_pinctrl_tries++; + } + + if ((state == chip->mmi.ebchg_state) && !force) { + if (eb_pwr_psy) + goto set_eb_done; + else + return; + } + + if (!eb_pwr_psy || !eb_pwr_psy->desc || + !eb_pwr_psy->desc->get_property) { + vote(chip->usb_icl_votable, EB_VOTER, + false, 0); + vote(chip->dc_suspend_votable, EB_VOTER, + true, 1); + if (gpio_is_valid(chip->mmi.ebchg_gpio.gpio)) + gpio_set_value(chip->mmi.ebchg_gpio.gpio, 0); + chip->mmi.cl_ebsrc = 0; + + ret.intval = MICRO_9V; + rc = smblib_set_prop_pd_voltage_max(chip, &ret); + if (rc < 0) + smblib_err(chip, + "Couldn't set 9V USBC Voltage rc=%d\n", rc); + chip->mmi.turbo_pwr_ebsrc = TURBO_EBSRC_UNKNOWN; + chip->mmi.ebchg_state = EB_DISCONN; + return; + } + + mmi_check_extbat_ability(chip, &ability); + + if ((state == EB_SINK) && (ability & EB_RCV_NEVER) && !force && + !(ability & EB_RCV_PARALLEL)) { + smblib_err(chip, + "Setting Sink State not Allowed on RVC Never EB\n"); + goto set_eb_done; + } + + if ((state == EB_SRC) && (ability & EB_SND_NEVER) && + !chip->mmi.usbeb_present && !force) { + smblib_err(chip, + "Setting SRC State not Allowed on SND Never EB\n"); + goto set_eb_done; + } + + mutex_lock(&chip->otg_lock); + if (chip->mmi.is_otg_enable == true && state == EB_SRC) { + smblib_err(chip, + "OTG is enable, so couldn't set SRC state\n"); + mutex_unlock(&chip->otg_lock); + goto set_eb_done; + } + mutex_unlock(&chip->otg_lock); + + smblib_dbg(chip, PR_MOTO, + "EB State is %d setting %d\n", + chip->mmi.ebchg_state, state); + + switch (state) { + case EB_SINK: + mmi_get_extbat_in_vl(chip); + + if (chip->mmi.vi_ebsrc < MICRO_9V) { + ret.intval = MICRO_5V; + rc = smblib_set_prop_pd_voltage_max(chip, &ret); + if (rc < 0) + smblib_err(chip, + "Couldn't set 5V USBC Voltage rc=%d\n", + rc); + msleep(500); + rc = smblib_get_prop_usb_voltage_now(chip, &ret); + if (rc < 0) { + smblib_err(chip, + "Couldn't get 5V USBC Voltage rc=%d\n", + rc); + goto set_eb_done; + } + if (ret.intval > (MICRO_5V + USBIN_TOLER)) { + smblib_err(chip, + "Voltage too high for EB_SINK %d uV\n", + ret.intval); + goto set_eb_done; + } + } + + mmi_set_extbat_in_volt(chip); + + ret.intval = POWER_SUPPLY_PTP_CURRENT_FROM_PHONE; + rc = power_supply_set_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_CURRENT_FLOW, + &ret); + if (!rc) { + chip->mmi.ebchg_state = state; + + vote(chip->usb_icl_votable, EB_VOTER, + false, 0); + vote(chip->dc_suspend_votable, EB_VOTER, + true, 1); + if (gpio_is_valid(chip->mmi.ebchg_gpio.gpio)) + gpio_set_value(chip->mmi.ebchg_gpio.gpio, 1); + } + break; + case EB_SRC: + if (!(ability & EB_SND_EXT) && (eb_attach_stop_soc != 100)) + chip->mmi.vl_ebsrc = MICRO_5V; + else + chip->mmi.vl_ebsrc = MICRO_9V; + + mmi_set_extbat_out_vl(chip); + ret.intval = POWER_SUPPLY_PTP_CURRENT_TO_PHONE; + rc = power_supply_set_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_CURRENT_FLOW, + &ret); + if (!rc) { + for (i = 0; i < 100; i++) { + rc = smblib_get_prop_dc_voltage_now(chip, + &ret); + if (rc == -EINVAL) + break; + dcin_mv = ret.intval / 1000; + pr_err("DCIN mv = %d\n", dcin_mv); + if (dcin_mv > 4500) + dcin_high_cnt++; + else + dcin_high_cnt = 0; + + if (dcin_high_cnt > 10) { + /* Total required delay is 200 msec + * for power mux transition timing. + */ + msleep(100); + break; + } + msleep(20); + } + + chip->mmi.ebchg_state = state; + vote(chip->usb_icl_votable, EB_VOTER, + true, 0); + vote(chip->dc_suspend_votable, EB_VOTER, + false, 1); + if (gpio_is_valid(chip->mmi.ebchg_gpio.gpio)) + gpio_set_value(chip->mmi.ebchg_gpio.gpio, 0); + + ret.intval = MICRO_9V; + rc = smblib_set_prop_pd_voltage_max(chip, &ret); + if (rc < 0) + smblib_err(chip, + "Couldn't set 9V USBC Voltage rc=%d\n", + rc); + } + break; + case EB_OFF: + ret.intval = POWER_SUPPLY_PTP_CURRENT_OFF; + rc = power_supply_set_property(eb_pwr_psy, + POWER_SUPPLY_PROP_PTP_CURRENT_FLOW, + &ret); + if (!rc) { + chip->mmi.ebchg_state = state; + if (gpio_is_valid(chip->mmi.ebchg_gpio.gpio)) + gpio_set_value(chip->mmi.ebchg_gpio.gpio, 0); + vote(chip->usb_icl_votable, EB_VOTER, + false, 0); + vote(chip->dc_suspend_votable, EB_VOTER, + true, 1); + + ret.intval = MICRO_9V; + rc = smblib_set_prop_pd_voltage_max(chip, &ret); + if (rc < 0) + smblib_err(chip, + "Couldn't set 9V USBC Voltage rc=%d\n", + rc); + } + + break; + default: + break; + } +set_eb_done: + power_supply_put(eb_pwr_psy); +} + +#define WEAK_CHRG_THRSH 450 +#define TURBO_CHRG_THRSH 2500 +void mmi_chrg_rate_check(struct smb_charger *chip) +{ + int rc; + union power_supply_propval val; + struct mmi_params *mmi = &chip->mmi; + int prev_chg_rate = mmi->charger_rate; + char *charge_rate[] = { + "None", "Normal", "Weak", "Turbo" + }; + + rc = smblib_get_prop_usb_present(chip, &val); + if (rc < 0) { + smblib_err(chip, "Error getting USB Present rc = %d\n", rc); + return; + } else if (!val.intval) { + if ((mmi->ebchg_state != EB_DISCONN) && + (mmi->turbo_pwr_ebsrc == TURBO_EBSRC_VALID)) + mmi->charger_rate = POWER_SUPPLY_CHARGE_RATE_TURBO; + else if (is_usbeb_present(chip)) { + if (mmi->cl_ebsrc >= TURBO_CHRG_THRSH) + mmi->charger_rate = + POWER_SUPPLY_CHARGE_RATE_TURBO; + else + mmi->charger_rate = + POWER_SUPPLY_CHARGE_RATE_NORMAL; + } else if (is_wls_present(chip)) + mmi->charger_rate = POWER_SUPPLY_CHARGE_RATE_NORMAL; + else + mmi->charger_rate = POWER_SUPPLY_CHARGE_RATE_NONE; + + goto end_rate_check; + } + + if (mmi->charger_rate == POWER_SUPPLY_CHARGE_RATE_TURBO) + goto end_rate_check; + + val.intval = get_client_vote(chip->usb_icl_votable, PD_VOTER); + if ((val.intval / 1000) >= TURBO_CHRG_THRSH) { + mmi->charger_rate = POWER_SUPPLY_CHARGE_RATE_TURBO; + goto end_rate_check; + } + + if (chip->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH || + mmi->hvdcp3_con) { + mmi->charger_rate = POWER_SUPPLY_CHARGE_RATE_TURBO; + goto end_rate_check; + } + + rc = smblib_get_prop_input_current_limited(chip, &val); + if (rc < 0) { + smblib_err(chip, "Error getting AICL Limited rc = %d\n", rc); + } else if (val.intval) { + int target_icl = get_effective_result(chip->usb_icl_votable); + rc = smblib_get_prop_input_current_settled(chip, &val); + if (rc < 0) { + smblib_err(chip, + "Error getting AICL Settled rc = %d\n", rc); + } else if (target_icl > 0 && val.intval < WEAK_CHRG_THRSH) { + mmi->charger_rate = POWER_SUPPLY_CHARGE_RATE_WEAK; + goto end_rate_check; + } + } + + mmi->charger_rate = POWER_SUPPLY_CHARGE_RATE_NORMAL; + +end_rate_check: + if (prev_chg_rate != mmi->charger_rate) + smblib_err(chip, "%s Charger Detected\n", + charge_rate[mmi->charger_rate]); + +} + +static int mmi_psy_notifier_call(struct notifier_block *nb, unsigned long val, + void *v) +{ + struct smb_charger *chip = container_of(nb, + struct smb_charger, mmi.mmi_psy_notifier); + struct power_supply *psy = v; + + if (!chip) { + smblib_dbg(chip, PR_MOTO, "called before chip valid!\n"); + return NOTIFY_DONE; + } + + if ((val == PSY_EVENT_PROP_ADDED) || + (val == PSY_EVENT_PROP_REMOVED)) { + smblib_dbg(chip, PR_MISC, "PSY Added/Removed run HB!\n"); + cancel_delayed_work(&chip->mmi.heartbeat_work); + schedule_delayed_work(&chip->mmi.heartbeat_work, + msecs_to_jiffies(0)); + return NOTIFY_OK; + } + + if (val != PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + if (psy && + (strcmp(psy->desc->name, + (char *)chip->mmi.eb_pwr_psy_name) == 0)) { + smblib_dbg(chip, PR_MISC, "PSY changed on PTP\n"); + cancel_delayed_work(&chip->mmi.heartbeat_work); + schedule_delayed_work(&chip->mmi.heartbeat_work, + msecs_to_jiffies(100)); + return NOTIFY_OK; + } + + return NOTIFY_OK; +} + +void update_charging_limit_modes(struct smb_charger *chip, int batt_soc) +{ + enum charging_limit_modes charging_limit_modes; + + charging_limit_modes = chip->mmi.charging_limit_modes; + if ((charging_limit_modes != CHARGING_LIMIT_RUN) + && (batt_soc >= chip->mmi.upper_limit_capacity)) + charging_limit_modes = CHARGING_LIMIT_RUN; + else if ((charging_limit_modes != CHARGING_LIMIT_OFF) + && (batt_soc <= chip->mmi.lower_limit_capacity)) + charging_limit_modes = CHARGING_LIMIT_OFF; + + if (charging_limit_modes != chip->mmi.charging_limit_modes) + chip->mmi.charging_limit_modes = charging_limit_modes; +} + +/* For Testing 50/50 Turbo Charge Split with EB */ +static int force_eb_fifty; +module_param(force_eb_fifty, int, 0644); + +#define CHARGER_DETECTION_DONE 7 +#define SMBCHG_HEARTBEAT_INTERVAL_NS 70000000000 +#define HEARTBEAT_DELAY_MS 60000 +#define HEARTBEAT_HOLDOFF_MS 10000 +#define HEARTBEAT_EB_WAIT_MS 1000 +#define HYST_STEP_MV 50 +#define EB_SPLIT_MA 500 +#define DEMO_MODE_HYS_SOC 5 +#define DEMO_MODE_VOLTAGE 4000 +#define VBUS_INPUT_VOLTAGE_TARGET 5200 +#define VBUS_INPUT_VOLTAGE_NOM ((VBUS_INPUT_VOLTAGE_TARGET) - 200) +#define VBUS_INPUT_VOLTAGE_MAX ((VBUS_INPUT_VOLTAGE_TARGET) + 200) +#define VBUS_INPUT_VOLTAGE_MIN 4000 +#define VBUS_INPUT_MAX_COUNT 4 +#define WARM_TEMP 45 +#define COOL_TEMP 0 +#define REV_BST_THRESH 4650 +#define REV_BST_DROP 150 +#define REV_BST_MA -10 +static void mmi_heartbeat_work(struct work_struct *work) +{ + struct smb_charger *chip = container_of(work, + struct smb_charger, + mmi.heartbeat_work.work); + int rc; + int batt_mv; + int batt_ma; + int batt_soc; + int eb_soc; + int batt_temp; + int usb_mv; + int dcin_mv; + u8 apsd_reg; + bool icl_override; + static int vbus_inc_mv = VBUS_INPUT_VOLTAGE_TARGET; + bool vbus_inc_now = false; + int vbus_present = 0; + int charger_present = 0; + int cl_usb = -EINVAL; + int cl_pd = -EINVAL; + int cl_cc = -EINVAL; + + int prev_step; + char eb_able = 0; + int hb_resch_time; + int ebparams_cnt = chip->mmi.update_eb_params; + bool eb_chrg_allowed; + + int eb_state = 0; + + int pwr_ext; + bool eb_sink_to_off = false; + + union power_supply_propval val; + struct mmi_params *mmi = &chip->mmi; + struct mmi_temp_zone *zone; + int max_fv_mv; + int target_usb; + int target_dc; + int target_fcc; + int target_fv; + int prev_vl_ebsrc = chip->mmi.vl_ebsrc; + int pok_irq; + static int prev_vbus_mv = -1; + + if (!atomic_read(&chip->mmi.hb_ready)) + return; + + /* Have not been resumed so wait another 100 ms */ + if (chip->suspended) { + smblib_err(chip, "SMB HB running before Resume\n"); + schedule_delayed_work(&mmi->heartbeat_work, + msecs_to_jiffies(300)); + return; + } + + vote(chip->awake_votable, HEARTBEAT_VOTER, true, true); + + alarm_try_to_cancel(&chip->mmi.heartbeat_alarm); + + /* Collect External Battery Information */ + eb_soc = get_eb_prop(chip, POWER_SUPPLY_PROP_CAPACITY); + pwr_ext = get_eb_pwr_prop(chip, POWER_SUPPLY_PROP_PTP_EXTERNAL); + mmi_check_extbat_ability(chip, &eb_able); + + if (eb_soc == -EINVAL) + eb_soc = 0; + + if ((eb_soc == -ENODEV) && (pwr_ext == -ENODEV)) + mmi_set_extbat_state(chip, EB_DISCONN, false); + else if (mmi->ebchg_state == EB_DISCONN) + mmi_set_extbat_state(chip, EB_OFF, false); + else { + rc = mmi_get_extbat_state(chip, &eb_state); + if ((rc >= 0) && (mmi->ebchg_state != eb_state)) + mmi_set_extbat_state(chip, EB_OFF, true); + } + + if (pwr_ext == POWER_SUPPLY_PTP_EXT_SUPPORTED) { + mmi->usbeb_present = is_usbeb_present(chip); + mmi->wls_present = is_wls_present(chip); + } else { + mmi->usbeb_present = false; + mmi->wls_present = false; + } + /* Hold Wakelock if External Battery has External Power */ + if ((mmi->wls_present || mmi->usbeb_present) && + (mmi->ebchg_state == EB_SRC)) + vote(chip->awake_votable, WIRELESS_VOTER, true, 0); + else + vote(chip->awake_votable, WIRELESS_VOTER, false, 0); + + if ((chip->mmi.ebchg_state == EB_SRC) && chip->mmi.check_ebsrc_vl) { + if (!(eb_able & EB_SND_EXT) && (eb_attach_stop_soc != 100)) + chip->mmi.vl_ebsrc = MICRO_5V; + else + chip->mmi.vl_ebsrc = MICRO_9V; + + if (prev_vl_ebsrc != chip->mmi.vl_ebsrc) + mmi_set_extbat_out_vl(chip); + } + chip->mmi.check_ebsrc_vl = false; + + /* Collect Current Information */ + rc = smblib_get_prop_usb_present(chip, &val); + if (rc < 0) { + pr_err("Error getting USB Present rc = %d\n", rc); + goto end_hb; + } else + vbus_present = val.intval; + + if (!vbus_present) { + vbus_inc_mv = VBUS_INPUT_VOLTAGE_TARGET; + charger_present = 0; + mmi->charger_debounce_cnt = 0; + if (mmi->apsd_done && + chip->typec_mode == POWER_SUPPLY_TYPEC_NONE) + typec_src_removal(chip); + } else if (mmi->charger_debounce_cnt < CHARGER_DETECTION_DONE) { + mmi->charger_debounce_cnt++; + /* Set the USB CL to 500 only if pd is not active to avoid + * PD Compliance issues */ + if (!chip->pd_active) + cl_usb = 500; + if ((chip->typec_mode == POWER_SUPPLY_TYPEC_NONE) || + (chip->typec_mode == + POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) || + (chip->pd_active && (chip->pd_contract_uv > 0))) { + mmi->charger_debounce_cnt = CHARGER_DETECTION_DONE; + charger_present = 1; + mmi->apsd_done = true; + } + } else if (mmi->charger_debounce_cnt == CHARGER_DETECTION_DONE) + charger_present = 1; + + rc = smblib_get_prop_from_bms(chip, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); + if (rc < 0) { + smblib_err(chip, "Error getting Batt Voltage rc = %d\n", rc); + goto end_hb; + } else + batt_mv = val.intval / 1000; + + rc = smblib_get_prop_from_bms(chip, + POWER_SUPPLY_PROP_CURRENT_NOW, &val); + if (rc < 0) { + smblib_err(chip, "Error getting Batt Current rc = %d\n", rc); + goto end_hb; + } else + batt_ma = val.intval / 1000; + + rc = smblib_get_prop_batt_capacity(chip, &val); + if (rc < 0) { + smblib_err(chip, "Error getting Batt Capacity rc = %d\n", rc); + goto end_hb; + } else + batt_soc = val.intval; + + rc = smblib_get_prop_from_bms(chip, + POWER_SUPPLY_PROP_TEMP, &val); + if (rc < 0) { + smblib_err(chip, + "Error getting Batt Temperature rc = %d\n", rc); + goto end_hb; + } else + batt_temp = val.intval / 10; + + rc = smblib_get_prop_usb_voltage_now(chip, &val); + if (rc < 0) { + smblib_err(chip, "Error getting USB Voltage rc = %d\n", rc); + goto end_hb; + } else + usb_mv = val.intval / 1000; + + rc = smblib_read(chip, APSD_RESULT_STATUS_REG, &apsd_reg); + if (rc < 0) { + smblib_err(chip, "Couldn't read APSD_RESULT_STATUS rc=%d\n", + rc); + icl_override = false; + apsd_reg = 0; + } else { + icl_override = !!(apsd_reg & APSD_RESULT_STATUS_7_BIT); + apsd_reg &= APSD_RESULT_STATUS_MASK; + } + + if (prev_vbus_mv == -1) + prev_vbus_mv = usb_mv; + + smblib_dbg(chip, PR_MISC, "batt=%d mV, %d mA, %d C, USB= %d mV\n", + batt_mv, batt_ma, batt_temp, usb_mv); + + pok_irq = chip->irq_info[SWITCHER_POWER_OK_IRQ].irq; + if (chip->reverse_boost || (batt_mv >= usb_mv && vbus_present)) { + if (((usb_mv < REV_BST_THRESH) && + ((prev_vbus_mv - REV_BST_DROP) > usb_mv)) || + (batt_ma > REV_BST_MA)) { + smblib_err(chip, + "Reverse Boosted: Clear, USB Suspend\n"); + if (chip->mmi.factory_mode) + smblib_set_usb_suspend(chip, true); + else + vote(chip->usb_icl_votable, BOOST_BACK_VOTER, + true, 0); + msleep(50); + if (chip->mmi.factory_mode) + smblib_set_usb_suspend(chip, + chip->mmi.force_chg_suspend); + else + vote(chip->usb_icl_votable, BOOST_BACK_VOTER, + false, 0); + if (chip->reverse_boost) { + chip->reverse_boost = false; + enable_irq(pok_irq); + } + } else { + smblib_err(chip, + "Reverse Boosted: USB %d mV PUSB %d mV\n", + usb_mv, prev_vbus_mv); + } + } + + prev_vbus_mv = usb_mv; + + if (charger_present) { + val.intval = get_client_vote(chip->usb_icl_votable, + USB_PSY_VOTER); + cl_usb = val.intval / 1000; + + val.intval = get_client_vote(chip->usb_icl_votable, + PD_VOTER); + cl_pd = val.intval / 1000; + + switch (chip->typec_mode) { + case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT: + if (mmi->hvdcp3_con) + cl_cc = 3000; + else + cl_cc = 500; + break; + case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM: + cl_cc = 1500; + break; + case POWER_SUPPLY_TYPEC_SOURCE_HIGH: + cl_cc = 3000; + break; + case POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER: + cl_cc = 500; + break; + case POWER_SUPPLY_TYPEC_NONE: + if (vbus_present) { + if (mmi->hvdcp3_con) + cl_cc = 3000; + else + cl_cc = 500; + } else + cl_cc = 0; + + if (vbus_present && + !icl_override && + (apsd_reg == 0)) { + rc = smblib_masked_write(chip, CMD_ICL_OVERRIDE_REG, + ICL_OVERRIDE_BIT, + ICL_OVERRIDE_BIT); + if (rc < 0) + smblib_err(chip, + "Fail ICL Over rc%d\n", rc); + } + break; + default: + cl_cc = 0; + break; + } + + if (cl_pd > 0) + cl_usb = cl_pd; + else if ((cl_cc > 500) || + (chip->typec_mode == + POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER)) + cl_usb = cl_cc; + else if (chip->real_charger_type == + POWER_SUPPLY_TYPE_USB_DCP) + // if (apsd_reg & OCP_CHARGER_BIT) + // cl_usb = 1000; + // else + cl_usb = 1800; + else if (chip->real_charger_type == + POWER_SUPPLY_TYPE_USB_CDP) + cl_usb = 1500; + else if ((cl_cc == 500) && + (cl_usb <= 500)) + cl_usb = cl_cc; + else if (cl_usb <= 0) + cl_usb = 500; + + if (cl_usb >= TURBO_CHRG_THRSH) + mmi->charger_rate = POWER_SUPPLY_CHARGE_RATE_TURBO; + } + + + /* Determine External Battery Next State */ + eb_chrg_allowed = (!(eb_able & EB_RCV_NEVER) && + !mmi->usbeb_present && + !mmi->wls_present); + + if (force_eb_fifty) + eb_able |= EB_RCV_PARALLEL; + + mmi_get_extbat_out_volt(chip); + mmi_get_extbat_out_cl(chip); + + if (mmi->enable_charging_limit && mmi->is_factory_image) + update_charging_limit_modes(chip, batt_soc); + + if (!vbus_present) { + switch (mmi->ebchg_state) { + case EB_SRC: + if (mmi->wls_present || mmi->usbeb_present) { + mmi->eb_rechrg = false; + if (batt_soc == 100) + mmi_set_extbat_state(chip, EB_OFF, + false); + } else if ((eb_able & EB_SND_NEVER) || + (eb_able & EB_SND_EXT) || + (eb_on_sw == 0)) { + mmi_set_extbat_state(chip, EB_OFF, false); + mmi->eb_rechrg = true; + } + + break; + case EB_SINK: + mmi_set_extbat_state(chip, EB_OFF, false); + eb_sink_to_off = true; + break; + case EB_OFF: + if (mmi->wls_present || mmi->usbeb_present) { + mmi->eb_rechrg = false; + if (batt_soc < 100) + mmi_set_extbat_state(chip, EB_SRC, + false); + } else if ((eb_able & EB_SND_NEVER) || + (eb_on_sw == 0)) + mmi_set_extbat_state(chip, EB_OFF, false); + else if (eb_able & EB_SND_LOW) { + if (batt_soc <= eb_low_start_soc) + mmi_set_extbat_state(chip, EB_SRC, + false); + else + mmi_set_extbat_state(chip, EB_OFF, + false); + } else + mmi_set_extbat_state(chip, EB_SRC, false); + + if ((mmi->ebchg_state == EB_SRC) && (batt_soc < 75)) + mmi->eb_rechrg = false; + + break; + case EB_DISCONN: + default: + break; + } + } else if (vbus_present || + mmi->wls_present || + mmi->usbeb_present) { + mmi->eb_rechrg = false; + } + + if (mmi->eb_rechrg) + target_dc = mmi->dc_eff_current_ma; + else if (mmi->cl_ebsrc) + target_dc = mmi->cl_ebsrc; + else + target_dc = mmi->dc_ebmax_current_ma; + + mmi_find_temp_zone(chip, batt_temp); + zone = &mmi->temp_zones[mmi->pres_temp_zone]; + + if (mmi->base_fv_mv == 0) { + mmi->base_fv_mv = get_client_vote(chip->fv_votable, + BATT_PROFILE_VOTER) / 1000; + vote(chip->fv_votable, + BATT_PROFILE_VOTER, false, 0); + } + max_fv_mv = mmi->base_fv_mv; + + rc = smblib_get_prop_dc_voltage_now(chip, &val); + if (rc == -EINVAL) { + dcin_mv = 0; + smblib_err(chip, "Failed to get DCIN\n"); + } else + dcin_mv = val.intval / 1000; + + /* Determine Next State */ + prev_step = chip->mmi.pres_chrg_step; + + if (mmi->charging_limit_modes == CHARGING_LIMIT_RUN) + pr_warn("Factory Mode/Image so Limiting Charging!!!\n"); + + if (!charger_present && (mmi->ebchg_state != EB_SRC)) { + mmi->pres_chrg_step = STEP_NONE; + } else if ((mmi->pres_temp_zone == ZONE_HOT) || + (mmi->pres_temp_zone == ZONE_COLD) || + (mmi->charging_limit_modes == CHARGING_LIMIT_RUN)) { + chip->mmi.pres_chrg_step = STEP_STOP; + } else if (mmi->force_eb_chrg && !(eb_able & EB_RCV_NEVER) && + (mmi->ebchg_state != EB_DISCONN)) { + mmi->pres_chrg_step = STEP_EB; + } else if (mmi->demo_mode) { + bool voltage_full; + static int demo_full_soc = 100; + int usb_suspend = get_client_vote(chip->usb_icl_votable, + DEMO_VOTER); + if (usb_suspend == -EINVAL) + usb_suspend = 0; + else if(usb_suspend == 0) + usb_suspend = 1; + + mmi->pres_chrg_step = STEP_DEMO; + smblib_dbg(chip, PR_MOTO, + "Battery in Demo Mode charging Limited %dper\n", + mmi->demo_mode); + + voltage_full = ((usb_suspend == 0) && + ((batt_mv + HYST_STEP_MV) >= DEMO_MODE_VOLTAGE) && + mmi_has_current_tapered(chip, batt_ma, + mmi->chrg_iterm)); + + if ((usb_suspend == 0) && + ((batt_soc >= mmi->demo_mode) || + voltage_full)) { + demo_full_soc = batt_soc; + vote(chip->usb_icl_votable, DEMO_VOTER, true, 0); + vote(chip->dc_suspend_votable, DEMO_VOTER, true, 0); + usb_suspend = 1; + } else if (usb_suspend && + (batt_soc <= + (demo_full_soc - DEMO_MODE_HYS_SOC))) { + if (chip->mmi.ebchg_state == EB_SINK) { + mmi_set_extbat_state(chip, EB_OFF, false); + mmi->cl_ebchg = 0; + mmi_set_extbat_in_cl(chip); + } + vote(chip->usb_icl_votable, DEMO_VOTER, false, 0); + vote(chip->dc_suspend_votable, DEMO_VOTER, false, 0); + usb_suspend = 0; + mmi->chrg_taper_cnt = 0; + } + + if (vbus_present && + ((eb_soc >= mmi->demo_mode) || + (eb_able & EB_RCV_NEVER))) { + mmi_set_extbat_state(chip, EB_OFF, false); + mmi->cl_ebchg = 0; + mmi_set_extbat_in_cl(chip); + } else if (vbus_present && usb_suspend && + (eb_soc <= + (mmi->demo_mode - DEMO_MODE_HYS_SOC))) { + mmi->cl_ebchg = cl_usb; + mmi_set_extbat_in_cl(chip); + mmi_set_extbat_state(chip, EB_SINK, false); + } + } else if ((mmi->pres_chrg_step == STEP_NONE) || + (mmi->pres_chrg_step == STEP_STOP)) { + if (zone->norm_mv && (batt_mv >= zone->norm_mv)) { + if (zone->fcc_norm_ma) + mmi->pres_chrg_step = STEP_NORM; + else + mmi->pres_chrg_step = STEP_STOP; + } else + mmi->pres_chrg_step = STEP_MAX; + } else if (mmi->pres_chrg_step == STEP_MAX) { + if (!zone->norm_mv) { + /* No Step in this Zone */ + mmi->chrg_taper_cnt = 0; + if ((batt_mv + HYST_STEP_MV) >= max_fv_mv) + mmi->pres_chrg_step = STEP_NORM; + else + mmi->pres_chrg_step = STEP_MAX; + } else if ((batt_mv + HYST_STEP_MV) < zone->norm_mv) { + mmi->chrg_taper_cnt = 0; + mmi->pres_chrg_step = STEP_MAX; + } else if (!zone->fcc_norm_ma) + mmi->pres_chrg_step = STEP_FLOAT; + else if (mmi_has_current_tapered(chip, batt_ma, + zone->fcc_norm_ma)) { + mmi->chrg_taper_cnt = 0; + if (smblib_charge_halted(chip)) { + vote(chip->chg_disable_votable, + HEARTBEAT_VOTER, true, 0); + smblib_err(chip, "Charge Halt..Toggle\n"); + msleep(50); + } + mmi->pres_chrg_step = STEP_NORM; + } + } else if (mmi->pres_chrg_step == STEP_NORM) { + if (!zone->fcc_norm_ma) + mmi->pres_chrg_step = STEP_STOP; + else if ((batt_soc < 100) || + (batt_mv + HYST_STEP_MV) < max_fv_mv) { + mmi->chrg_taper_cnt = 0; + mmi->pres_chrg_step = STEP_NORM; + } else if (mmi_has_current_tapered(chip, batt_ma, + mmi->chrg_iterm)) { + if (eb_chrg_allowed) + mmi->pres_chrg_step = STEP_EB; + else + mmi->pres_chrg_step = STEP_FULL; + } + } else if (mmi->pres_chrg_step == STEP_EB) { + if ((batt_soc < 95) || !eb_chrg_allowed) { + mmi->chrg_taper_cnt = 0; + mmi->pres_chrg_step = STEP_NORM; + } + } else if (mmi->pres_chrg_step == STEP_FULL) { + if (batt_soc <= 99) { + mmi->chrg_taper_cnt = 0; + mmi->pres_chrg_step = STEP_NORM; + } else if (eb_chrg_allowed) { + mmi->pres_chrg_step = STEP_EB; + } + } else if (mmi->pres_chrg_step == STEP_FLOAT) { + if ((zone->fcc_norm_ma) || + ((batt_mv + HYST_STEP_MV) < zone->norm_mv)) + mmi->pres_chrg_step = STEP_MAX; + + } + + /* Take State actions */ + switch (mmi->pres_chrg_step) { + case STEP_EB: + target_fv = max_fv_mv; + + if (cl_usb > 1500) { + mmi->cl_ebchg = cl_usb - EB_SPLIT_MA; + target_usb = cl_usb; + if (batt_soc < 100) + target_fcc = min(zone->fcc_norm_ma, EB_SPLIT_MA); + else + target_fcc = -EINVAL; + } else { + mmi->cl_ebchg = cl_usb; + target_usb = cl_usb; + target_fcc = -EINVAL; + } + + break; + case STEP_FLOAT: + case STEP_MAX: + if (!zone->norm_mv) + target_fv = max_fv_mv; + else + target_fv = zone->norm_mv; + target_fcc = zone->fcc_max_ma; + mmi->cl_ebchg = 0; + + if (((cl_usb - EB_SPLIT_MA) >= target_fcc) && eb_chrg_allowed) + mmi->cl_ebchg = EB_SPLIT_MA; + else if (eb_chrg_allowed && (eb_able & EB_RCV_PARALLEL) && + (mmi->charger_rate == POWER_SUPPLY_CHARGE_RATE_TURBO)) + mmi->cl_ebchg = cl_usb / 2; /* 50/50 Split */ + else if ((eb_able & EB_RCV_PARALLEL) && + (mmi->charger_rate == + POWER_SUPPLY_CHARGE_RATE_TURBO) && + !mmi->usbeb_present) + mmi->cl_ebchg = EB_SPLIT_MA; + + target_usb = cl_usb; + break; + case STEP_FULL: + target_fv = max_fv_mv; + target_fcc = -EINVAL; + mmi->cl_ebchg = 0; + + if ((eb_able & EB_RCV_PARALLEL) && + (mmi->charger_rate == + POWER_SUPPLY_CHARGE_RATE_TURBO) && + !mmi->usbeb_present) + mmi->cl_ebchg = EB_SPLIT_MA; + + target_usb = cl_usb; + break; + case STEP_NORM: + target_fv = max_fv_mv + mmi->vfloat_comp_mv; + target_fcc = zone->fcc_norm_ma; + mmi->cl_ebchg = 0; + + if (((cl_usb - EB_SPLIT_MA) >= target_fcc) && eb_chrg_allowed) + mmi->cl_ebchg = EB_SPLIT_MA; + else if (eb_chrg_allowed && (eb_able & EB_RCV_PARALLEL) && + (mmi->charger_rate == POWER_SUPPLY_CHARGE_RATE_TURBO)) + mmi->cl_ebchg = cl_usb / 2; /* 50/50 Split */ + else if ((eb_able & EB_RCV_PARALLEL) && + (mmi->charger_rate == + POWER_SUPPLY_CHARGE_RATE_TURBO) && + !mmi->usbeb_present) + mmi->cl_ebchg = EB_SPLIT_MA; + + target_usb = cl_usb; + break; + case STEP_NONE: + target_fv = max_fv_mv; + target_fcc = zone->fcc_norm_ma; + target_usb = cl_usb; + break; + case STEP_STOP: + target_fv = max_fv_mv; + target_fcc = -EINVAL; + target_usb = cl_usb; + break; + case STEP_DEMO: + target_fv = DEMO_MODE_VOLTAGE; + target_fcc = zone->fcc_max_ma; + target_usb = cl_usb; + break; + default: + break; + } + + /* Votes for State */ + vote(chip->fv_votable, HEARTBEAT_VOTER, true, target_fv * 1000); + + vote(chip->chg_disable_votable, HEARTBEAT_VOTER, + (target_fcc < 0), 0); + + vote(chip->fcc_votable, HEARTBEAT_VOTER, + true, (target_fcc >= 0) ? (target_fcc * 1000) : 0); + + rc = vote(chip->usb_icl_votable, HEARTBEAT_VOTER, + true, (target_usb >= 0) ? (target_usb * 1000) : 0); + if (rc < 0) { + smblib_err(chip, "Problem setting USB ICL %d\n", target_usb); + goto end_hb; + } + + if (vbus_present) { + if (mmi->cl_ebchg > 0) { + mmi_set_extbat_in_cl(chip); + mmi_set_extbat_state(chip, EB_SINK, false); + } else if (mmi->ebchg_state != EB_DISCONN) + mmi_set_extbat_state(chip, EB_OFF, false); + } + + if ((target_dc > 1600) && + (chip->mmi.vl_ebsrc >= MICRO_9V) && + (dcin_mv > ((MICRO_5V / 1000) + 500))) + target_dc = 1600; + + vote(chip->dc_icl_votable, HEARTBEAT_VOTER, + true, (target_dc >= 0) ? (target_dc * 1000) : 0); + + if (charger_present && mmi->hvdcp3_con && !chip->pd_active && + (vbus_inc_mv > VBUS_INPUT_VOLTAGE_NOM)) { + if ((usb_mv < vbus_inc_mv) && + (usb_mv >= VBUS_INPUT_VOLTAGE_MIN) && + (mmi->vbus_inc_cnt < VBUS_INPUT_MAX_COUNT)) { + smblib_dbg(chip, PR_MOTO, + "HVDCP Input %d mV Low, Increase\n", + usb_mv); + smblib_dp_pulse(chip); + vbus_inc_now = true; + mmi->vbus_inc_cnt++; + } else if ((usb_mv > VBUS_INPUT_VOLTAGE_MAX) + && (mmi->vbus_inc_cnt > 0)) { + smblib_dbg(chip, PR_MOTO, + "HVDCP Input %d mV High, Decrease\n", + usb_mv); + vbus_inc_mv -= 50; + smblib_dm_pulse(chip); + vbus_inc_now = true; + mmi->vbus_inc_cnt--; + } + } + + if (chip->mmi.pres_temp_zone == ZONE_HOT) { + chip->mmi.batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; + } else if (chip->mmi.pres_temp_zone == ZONE_COLD) { + chip->mmi.batt_health = POWER_SUPPLY_HEALTH_COLD; + } else if (batt_temp >= WARM_TEMP) { + if (chip->mmi.pres_chrg_step == STEP_STOP) + chip->mmi.batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; + else + chip->mmi.batt_health = POWER_SUPPLY_HEALTH_GOOD; + } else if (batt_temp <= COOL_TEMP) { + if (chip->mmi.pres_chrg_step == STEP_STOP) + chip->mmi.batt_health = POWER_SUPPLY_HEALTH_COLD; + else + chip->mmi.batt_health = POWER_SUPPLY_HEALTH_GOOD; + } else + chip->mmi.batt_health = POWER_SUPPLY_HEALTH_GOOD; + + smblib_dbg(chip, PR_MOTO, + "EB Input %d mA, PMI Input %d mA, USBC CL %d mA, DC %d mA\n", + mmi->cl_ebchg, target_usb, cl_cc, + target_dc); + smblib_dbg(chip, PR_MOTO, "Step State = %s, EB State %s\n", + stepchg_str[(int)mmi->pres_chrg_step], + ebchg_str[(int)mmi->ebchg_state]); + smblib_dbg(chip, PR_MOTO, + "EFFECTIVE: FV = %d, CDIS = %d, FCC = %d," + "USBICL = %d, DCICL = %d\n", + get_effective_result(chip->fv_votable), + get_effective_result(chip->chg_disable_votable), + get_effective_result(chip->fcc_votable), + get_effective_result(chip->usb_icl_votable), + get_effective_result(chip->dc_icl_votable)); +end_hb: + if (chip->batt_psy) + power_supply_changed(chip->batt_psy); + + hb_resch_time = HEARTBEAT_HOLDOFF_MS; + + if (!chip->mmi.chrg_taper_cnt && (rc >= 0)) + hb_resch_time = HEARTBEAT_DELAY_MS; + + if (ebparams_cnt != mmi->update_eb_params) + hb_resch_time = HEARTBEAT_EB_MS; + else + mmi->update_eb_params = 0; + + if (eb_sink_to_off || + (!charger_present && mmi->charger_debounce_cnt) || + vbus_inc_now) + hb_resch_time = HEARTBEAT_EB_WAIT_MS; + + schedule_delayed_work(&mmi->heartbeat_work, + msecs_to_jiffies(hb_resch_time)); + + if (mmi->ebchg_state == EB_SRC) { + alarm_start_relative(&mmi->heartbeat_alarm, + ns_to_ktime(SMBCHG_HEARTBEAT_INTERVAL_NS)); + } + + __pm_relax(&mmi->smblib_mmi_hb_wake_source); + if (!vbus_present) + vote(chip->awake_votable, HEARTBEAT_VOTER, false, 0); +} +static int smbchg_reboot(struct notifier_block *nb, + unsigned long event, void *unused) +{ + struct smb_charger *chg = container_of(nb, struct smb_charger, + mmi.smb_reboot); + union power_supply_propval val; + int rc; + char eb_able; + int usb_present = -EINVAL, batt_soc = -EINVAL; + int soc_max = 99; + + smblib_dbg(chg, PR_MISC, "SMB Reboot\n"); + if (!chg) { + smblib_dbg(chg, PR_MOTO, "called before chip valid!\n"); + return NOTIFY_DONE; + } + + mmi_check_extbat_ability(chg, &eb_able); + if (eb_able & EB_SND_NEVER) + soc_max = 0; + else if (eb_able & EB_SND_LOW) + soc_max = eb_low_start_soc; + + atomic_set(&chg->mmi.hb_ready, 0); + cancel_delayed_work_sync(&chg->mmi.heartbeat_work); + + rc = smblib_get_prop_usb_present(chg, &val); + if (rc >= 0) + usb_present = val.intval; + + val.intval = 0; + rc = smblib_get_prop_batt_capacity(chg, &val); + if (rc >= 0) + batt_soc = val.intval; + + if (chg->mmi.factory_mode) { + switch (event) { + case SYS_POWER_OFF: + /* Disable Factory Kill */ + factory_kill_disable = true; + /* Disable Charging */ + smblib_masked_write(chg, CHARGING_ENABLE_CMD_REG, + CHARGING_ENABLE_CMD_BIT, + 0); + + /* Suspend USB and DC */ + smblib_set_usb_suspend(chg, true); + smblib_set_dc_suspend(chg, true); + + rc = smblib_get_prop_usb_present(chg, &val); + while (rc >= 0 && val.intval) { + msleep(100); + rc = smblib_get_prop_usb_present(chg, &val); + smblib_dbg(chg, PR_MOTO, + "Wait for VBUS to decay\n"); + } + smblib_dbg(chg, PR_MOTO, "VBUS UV wait 1 sec!\n"); + /* Delay 1 sec to allow more VBUS decay */ + msleep(1000); + /* configure power role for UFP */ + smblib_masked_write(chg, + TYPE_C_MODE_CFG_REG, + TYPEC_POWER_ROLE_CMD_MASK, + EN_SNK_ONLY_BIT); + break; + default: + break; + } + } else if ((batt_soc >= soc_max) || + ((get_eb_prop(chg, POWER_SUPPLY_PROP_CAPACITY) <= 0) && + (is_eb_present(chg) == true))) { + /* Turn off any Ext batt charging */ + smblib_err(chg, "Attempt to Shutdown EB!\n"); + mmi_set_extbat_state(chg, EB_OFF, false); + if (gpio_is_valid(chg->mmi.ebchg_gpio.gpio)) { + gpio_set_value(chg->mmi.ebchg_gpio.gpio, 0); + gpio_free(chg->mmi.ebchg_gpio.gpio); + } + } else if ((chg->mmi.ebchg_state == EB_OFF) && (usb_present == 0)) { + smblib_err(chg, "Attempt to Turn EB ON!\n"); + mmi_set_extbat_state(chg, EB_SRC, false); + } + + return NOTIFY_DONE; +} + +#define CHG_SHOW_MAX_SIZE 50 +static ssize_t factory_image_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("Invalid factory image mode value = %lu\n", mode); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("chip not valid\n"); + return -ENODEV; + } + + mmi_chip->mmi.is_factory_image = (mode) ? true : false; + + return r ? r : count; +} + +static ssize_t factory_image_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + + if (!mmi_chip) { + pr_err("chip not valid\n"); + return -ENODEV; + } + + state = (mmi_chip->mmi.is_factory_image) ? 1 : 0; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +ssize_t mmi_get_factory_image_mode(void) +{ + if (!mmi_chip) { + pr_err("chip not valid\n"); + return -ENODEV; + } + + return mmi_chip->mmi.is_factory_image; +} + +ssize_t mmi_set_factory_image_mode(int mode) +{ + if (!mmi_chip) { + pr_err("chip not valid\n"); + return -ENODEV; + } + + mmi_chip->mmi.is_factory_image = (mode) ? true : false; + return 0; +} +EXPORT_SYMBOL(mmi_get_factory_image_mode); +EXPORT_SYMBOL(mmi_set_factory_image_mode); + +static DEVICE_ATTR(factory_image_mode, 0644, + factory_image_mode_show, + factory_image_mode_store); + +static ssize_t factory_charge_upper_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + + if (!mmi_chip) { + pr_err("chip not valid\n"); + return -ENODEV; + } + + state = mmi_chip->mmi.upper_limit_capacity; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +ssize_t mmi_get_factory_charge_upper(void) +{ + if (!mmi_chip) { + pr_err("chip not valid\n"); + return -ENODEV; + } + + return mmi_chip->mmi.upper_limit_capacity; +} +EXPORT_SYMBOL(mmi_get_factory_charge_upper); + +static DEVICE_ATTR(factory_charge_upper, 0444, + factory_charge_upper_show, + NULL); + +static ssize_t force_demo_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + r = kstrtoul(buf, 0, &mode); + if (r) { + smblib_err(mmi_chip, "Invalid demo mode value = %lu\n", mode); + return -EINVAL; + } + + if (!mmi_chip) { + smblib_err(mmi_chip, "chip not valid\n"); + return -ENODEV; + } + mmi_chip->mmi.chrg_taper_cnt = 0; + + if ((mode >= 35) && (mode <= 80)) + mmi_chip->mmi.demo_mode = mode; + else + mmi_chip->mmi.demo_mode = 35; + + return r ? r : count; +} + +static ssize_t force_demo_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + + if (!mmi_chip) { + smblib_err(mmi_chip, "chip not valid\n"); + return -ENODEV; + } + + state = mmi_chip->mmi.demo_mode; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +ssize_t mmi_get_demo_mode(void) +{ + if (!mmi_chip) { + pr_err("chip not valid\n"); + return -ENODEV; + } + + return mmi_chip->mmi.demo_mode; +} + +ssize_t mmi_set_demo_mode(int mode) +{ + if (!mmi_chip) { + pr_err("chip not valid\n"); + return -ENODEV; + } + + mmi_chip->mmi.chrg_taper_cnt = 0; + if ((mode >= 35) && (mode <= 80)) + mmi_chip->mmi.demo_mode = mode; + else + mmi_chip->mmi.demo_mode = 35; + + return 0; +} +EXPORT_SYMBOL(mmi_get_demo_mode); +EXPORT_SYMBOL(mmi_set_demo_mode); + +static DEVICE_ATTR(force_demo_mode, 0644, + force_demo_mode_show, + force_demo_mode_store); + +static ssize_t force_max_chrg_temp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + r = kstrtoul(buf, 0, &mode); + if (r) { + smblib_err(mmi_chip, "Invalid max temp value = %lu\n", mode); + return -EINVAL; + } + + if (!mmi_chip) { + smblib_err(mmi_chip, "chip not valid\n"); + return -ENODEV; + } + + if ((mode >= MIN_MAX_TEMP_C) && (mode <= MAX_TEMP_C)) + mmi_chip->mmi.max_chrg_temp = mode; + else + mmi_chip->mmi.max_chrg_temp = MAX_TEMP_C; + + return r ? r : count; +} + +static ssize_t force_max_chrg_temp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + + if (!mmi_chip) { + smblib_err(mmi_chip, "chip not valid\n"); + return -ENODEV; + } + + state = mmi_chip->mmi.max_chrg_temp; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +ssize_t mmi_get_max_chrg_temp(void) +{ + if (!mmi_chip) { + pr_err("chip not valid\n"); + return -ENODEV; + } + + return mmi_chip->mmi.max_chrg_temp; +} + +ssize_t mmi_set_max_chrg_temp(int value) +{ + if (!mmi_chip) { + pr_err("chip not valid\n"); + return -ENODEV; + } + + if ((value >= MIN_MAX_TEMP_C) && (value <= MAX_TEMP_C)) + mmi_chip->mmi.max_chrg_temp = value; + else + mmi_chip->mmi.max_chrg_temp = MAX_TEMP_C; + return 0; +} +EXPORT_SYMBOL(mmi_set_max_chrg_temp); +EXPORT_SYMBOL(mmi_get_max_chrg_temp); + +static DEVICE_ATTR(force_max_chrg_temp, 0644, + force_max_chrg_temp_show, + force_max_chrg_temp_store); + +static ssize_t force_chg_usb_suspend_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + r = kstrtoul(buf, 0, &mode); + if (r) { + smblib_err(mmi_chip, + "Invalid usb suspend mode value = %lu\n", mode); + return -EINVAL; + } + + if (!mmi_chip) { + smblib_err(mmi_chip, "chip not valid\n"); + return -ENODEV; + } + mmi_chip->mmi.force_chg_suspend = (bool)mode; + r = smblib_set_usb_suspend(mmi_chip, (bool)mode); + if (r) + return r; + r = smblib_set_dc_suspend(mmi_chip, (bool)mode); + + return r ? r : count; +} + +static ssize_t force_chg_usb_suspend_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int ret; + + if (!mmi_chip) { + smblib_err(mmi_chip, "chip not valid\n"); + return -ENODEV; + } + ret = smblib_get_usb_suspend(mmi_chip, &state); + if (ret) { + smblib_err(mmi_chip, + "USBIN_SUSPEND_BIT failed ret = %d\n", ret); + state = -EFAULT; + goto end; + } + +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_usb_suspend, 0664, + force_chg_usb_suspend_show, + force_chg_usb_suspend_store); + +static ssize_t force_chg_fail_clear_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + r = kstrtoul(buf, 0, &mode); + if (r) { + smblib_err(mmi_chip, + "Invalid chg fail mode value = %lu\n", mode); + return -EINVAL; + } + + /* do nothing for SMBCHG */ + r = 0; + + return r ? r : count; +} + +static ssize_t force_chg_fail_clear_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + /* do nothing for SMBCHG */ + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "0\n"); +} + +static DEVICE_ATTR(force_chg_fail_clear, 0664, + force_chg_fail_clear_show, + force_chg_fail_clear_store); + +static ssize_t force_chg_auto_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + r = kstrtoul(buf, 0, &mode); + if (r) { + smblib_err(mmi_chip, + "Invalid chrg enable value = %lu\n", mode); + return -EINVAL; + } + + if (!mmi_chip) { + smblib_err(mmi_chip, "chip not valid\n"); + return -ENODEV; + } + + r = smblib_masked_write(mmi_chip, CHARGING_ENABLE_CMD_REG, + CHARGING_ENABLE_CMD_BIT, + mode ? CHARGING_ENABLE_CMD_BIT : 0); + if (r < 0) { + smblib_err(mmi_chip, "Factory Couldn't %s charging rc=%d\n", + mode ? "disable" : "enable", (int)r); + return r; + } + + return r ? r : count; +} + +static ssize_t force_chg_auto_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int ret; + u8 value; + + if (!mmi_chip) { + smblib_err(mmi_chip, "chip not valid\n"); + state = -ENODEV; + goto end; + } + + ret = smblib_read(mmi_chip, CHARGING_ENABLE_CMD_REG, &value); + if (ret) { + smblib_err(mmi_chip, "CHG_EN_BIT failed ret = %d\n", ret); + state = -EFAULT; + goto end; + } + + state = (CHARGING_ENABLE_CMD_BIT & value) ? 1 : 0; +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_auto_enable, 0664, + force_chg_auto_enable_show, + force_chg_auto_enable_store); + +static ssize_t force_chg_ibatt_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long chg_current; + + r = kstrtoul(buf, 0, &chg_current); + if (r) { + smblib_err(mmi_chip, + "Invalid ibatt value = %lu\n", chg_current); + return -EINVAL; + } + + if (!mmi_chip) { + smblib_err(mmi_chip, "chip not valid\n"); + return -ENODEV; + } + + chg_current *= 1000; /* Convert to uA */ + r = smblib_set_charge_param(mmi_chip, &mmi_chip->param.fcc, chg_current); + if (r < 0) { + smblib_err(mmi_chip, + "Factory Couldn't set masterfcc = %d rc=%d\n", + (int)chg_current, (int)r); + return r; + } + + return r ? r : count; +} + +static ssize_t force_chg_ibatt_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int ret; + + if (!mmi_chip) { + smblib_err(mmi_chip, "chip not valid\n"); + state = -ENODEV; + goto end; + } + + ret = smblib_get_charge_param(mmi_chip, &mmi_chip->param.fcc, &state); + if (ret < 0) { + smblib_err(mmi_chip, + "Factory Couldn't get master fcc rc=%d\n", + (int)ret); + return ret; + } + + state /= 1000; /* Convert to mA */ +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_ibatt, 0664, + force_chg_ibatt_show, + force_chg_ibatt_store); + +static ssize_t force_chg_iusb_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long usb_curr; + + r = kstrtoul(buf, 0, &usb_curr); + if (r) { + smblib_err(mmi_chip, "Invalid iusb value = %lu\n", usb_curr); + return -EINVAL; + } + + if (!mmi_chip) { + smblib_err(mmi_chip, "chip not valid\n"); + return -ENODEV; + } + usb_curr *= 1000; /* Convert to uA */ + r = smblib_set_charge_param(mmi_chip, &mmi_chip->param.usb_icl, usb_curr); + if (r < 0) { + smblib_err(mmi_chip, + "Factory Couldn't set usb icl = %d rc=%d\n", + (int)usb_curr, (int)r); + return r; + } + + return r ? r : count; +} + +static ssize_t force_chg_iusb_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int r; + + if (!mmi_chip) { + smblib_err(mmi_chip, "chip not valid\n"); + r = -ENODEV; + goto end; + } + + r = smblib_get_charge_param(mmi_chip, &mmi_chip->param.usb_icl, &state); + if (r < 0) { + smblib_err(mmi_chip, + "Factory Couldn't get usb_icl rc=%d\n", (int)r); + return r; + } + state /= 1000; /* Convert to mA */ +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_iusb, 0664, + force_chg_iusb_show, + force_chg_iusb_store); + +#define PRE_CHARGE_CONV_MV 50 +#define PRE_CHARGE_MAX 0x7 +static ssize_t force_chg_itrick_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long chg_current; + u8 value; + + r = kstrtoul(buf, 0, &chg_current); + if (r) { + smblib_err(mmi_chip, + "Invalid pre-charge value = %lu\n", chg_current); + return -EINVAL; + } + + if (!mmi_chip) { + smblib_err(mmi_chip, "chip not valid\n"); + return -ENODEV; + } + + chg_current /= PRE_CHARGE_CONV_MV; + + if (chg_current > PRE_CHARGE_MAX) + value = PRE_CHARGE_MAX; + else + value = (u8)chg_current; + + r = smblib_masked_write(mmi_chip, CHGR_PRE_CHARGE_CURRENT_CFG_REG, + CHGR_PRE_CHARGE_CURRENT_SETTING_MASK, + value); + if (r < 0) { + smblib_err(mmi_chip, + "Factory Couldn't set ITRICK %d mV rc=%d\n", + (int)value, (int)r); + return r; + } + + return r ? r : count; +} + +static ssize_t force_chg_itrick_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int ret; + u8 value; + + if (!mmi_chip) { + smblib_err(mmi_chip, "chip not valid\n"); + state = -ENODEV; + goto end; + } + + ret = smblib_read(mmi_chip, CHGR_PRE_CHARGE_CURRENT_CFG_REG, &value); + if (ret) { + smblib_err(mmi_chip, "Pre Chg ITrick failed ret = %d\n", ret); + state = -EFAULT; + goto end; + } + + value &= CHGR_PRE_CHARGE_CURRENT_SETTING_MASK; + + state = value * PRE_CHARGE_CONV_MV; + +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_itrick, 0664, + force_chg_itrick_show, + force_chg_itrick_store); + +int smblib_get_prop_dc_system_temp_level(struct smb_charger *chg, + union power_supply_propval *val) +{ + val->intval = chg->mmi.dc_system_temp_level; + return 0; +} + +int smblib_set_prop_dc_system_temp_level(struct smb_charger *chg, + const union power_supply_propval *val) +{ + if (val->intval < 0) + return -EINVAL; + + if (chg->mmi.dc_thermal_levels <= 0) + return -EINVAL; + + if (val->intval >= chg->mmi.dc_thermal_levels) + chg->mmi.dc_system_temp_level = chg->mmi.dc_thermal_levels - 1; + else + chg->mmi.dc_system_temp_level = val->intval; + + if (chg->mmi.dc_system_temp_level == 0) + return vote(chg->dc_icl_votable, THERMAL_DAEMON_VOTER, false, 0); + + vote(chg->dc_icl_votable, THERMAL_DAEMON_VOTER, true, + chg->mmi.dc_thermal_mitigation[chg->mmi.dc_system_temp_level] * 1000); + + return 0; +} + +int smblib_get_prop_usb_system_temp_level(struct smb_charger *chg, + union power_supply_propval *val) +{ + val->intval = chg->mmi.usb_system_temp_level; + return 0; +} + +int smblib_set_prop_usb_system_temp_level(struct smb_charger *chg, + const union power_supply_propval *val) +{ + if (val->intval < 0) + return -EINVAL; + + if (chg->mmi.usb_thermal_levels <= 0) + return -EINVAL; + + if (chg->mmi.usb_system_temp_level >= chg->mmi.usb_thermal_levels) + chg->mmi.usb_system_temp_level = chg->mmi.usb_thermal_levels - 1; + else + chg->mmi.usb_system_temp_level = val->intval; + + if (chg->mmi.usb_system_temp_level == 0) + return vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, false, 0); + + vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, true, + chg->mmi.usb_thermal_mitigation[chg->mmi.usb_system_temp_level] * 1000); + + return 0; +} +static bool mmi_factory_check(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool factory = false; + + if (np) + factory = of_property_read_bool(np, "mmi,factory-cable"); + + of_node_put(np); + + return factory; +} + +static void parse_mmi_dt_gpio(struct smb_charger *chg) +{ + struct device_node *node = chg->dev->of_node; + enum of_gpio_flags flags; + int rc; + + chg->mmi.ebchg_gpio.gpio = -EINVAL; + + if (!node) { + smblib_err(chg, "gpio dtree info. missing\n"); + return; + } + + if (of_gpio_count(node) < 0) { + smblib_err(chg, "No GPIOS defined.\n"); + return; + } + + chg->mmi.ebchg_gpio.gpio = of_get_gpio_flags(node, 0, &flags); + chg->mmi.ebchg_gpio.flags = flags; + of_property_read_string_index(node, "gpio-names", 0, + &chg->mmi.ebchg_gpio.label); + + if (!gpio_is_valid(chg->mmi.ebchg_gpio.gpio)) { + dev_err(chg->dev, "get gpio ebchg error rc=%d\n", chg->mmi.ebchg_gpio.gpio); + return; + } + + rc = gpio_request_one(chg->mmi.ebchg_gpio.gpio, + chg->mmi.ebchg_gpio.flags, + chg->mmi.ebchg_gpio.label); + if (rc) { + smblib_err(chg, "failed to request eb GPIO\n"); + return; + } + + rc = gpio_export(chg->mmi.ebchg_gpio.gpio, 1); + if (rc) { + smblib_err(chg, "Failed to export eb GPIO %s: %d\n", + chg->mmi.ebchg_gpio.label, + chg->mmi.ebchg_gpio.gpio); + return; + } + + rc = gpio_export_link(chg->dev, chg->mmi.ebchg_gpio.label, + chg->mmi.ebchg_gpio.gpio); + if (rc) + smblib_err(chg, "Failed to eb link GPIO %s: %d\n", + chg->mmi.ebchg_gpio.label, + chg->mmi.ebchg_gpio.gpio); + +} + +static int parse_mmi_dt(struct smb_charger *chg) +{ + struct device_node *node = chg->dev->of_node; + int rc = 0; + int byte_len; + int i; + + if (!node) { + smblib_err(chg, "mmi dtree info. missing\n"); + return -ENODEV; + } + + if (of_find_property(node, "qcom,mmi-temp-zones", &byte_len)) { + if ((byte_len / sizeof(u32)) % 4) { + smblib_err(chg, + "DT error wrong mmi temp zones\n"); + return -ENODEV; + } + + chg->mmi.temp_zones = (struct mmi_temp_zone *) + devm_kzalloc(chg->dev, byte_len, GFP_KERNEL); + + if (chg->mmi.temp_zones == NULL) + return -ENOMEM; + + chg->mmi.num_temp_zones = + byte_len / sizeof(struct mmi_temp_zone); + + rc = of_property_read_u32_array(node, + "qcom,mmi-temp-zones", + (u32 *)chg->mmi.temp_zones, + byte_len / sizeof(u32)); + if (rc < 0) { + smblib_err(chg, + "Couldn't read mmi temp zones rc = %d\n", + rc); + return rc; + } + smblib_err(chg, "mmi temp zones: Num: %d\n", + chg->mmi.num_temp_zones); + for (i = 0; i < chg->mmi.num_temp_zones; i++) { + smblib_err(chg, + "mmi temp zones: Zone %d, Temp %d C, " \ + "Step Volt %d mV, Full Rate %d mA, " \ + "Taper Rate %d mA\n", i, + chg->mmi.temp_zones[i].temp_c, + chg->mmi.temp_zones[i].norm_mv, + chg->mmi.temp_zones[i].fcc_max_ma, + chg->mmi.temp_zones[i].fcc_norm_ma); + } + chg->mmi.pres_temp_zone = ZONE_NONE; + } + + if (of_find_property(node, "qcom,usb-thermal-mitigation", &byte_len)) { + chg->mmi.usb_thermal_mitigation = + devm_kzalloc(chg->dev, byte_len, GFP_KERNEL); + + if (chg->mmi.usb_thermal_mitigation == NULL) + return -ENOMEM; + + chg->mmi.usb_thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,usb-thermal-mitigation", + chg->mmi.usb_thermal_mitigation, + chg->mmi.usb_thermal_levels); + if (rc < 0) { + smblib_err(chg, + "Couldn't read usb therm limits rc = %d\n", + rc); + return rc; + } + } else { + chg->mmi.usb_thermal_mitigation = NULL; + chg->mmi.usb_thermal_levels = 0; + dev_err(chg->dev, "usb-thermal-mitigation is not set\n"); + } + + if (of_find_property(node, "qcom,dc-thermal-mitigation", &byte_len)) { + chg->mmi.dc_thermal_mitigation = + devm_kzalloc(chg->dev, byte_len, GFP_KERNEL); + + if (chg->mmi.dc_thermal_mitigation == NULL) + return -ENOMEM; + + chg->mmi.dc_thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,dc-thermal-mitigation", + chg->mmi.dc_thermal_mitigation, + chg->mmi.dc_thermal_levels); + if (rc < 0) { + smblib_err(chg, + "Couldn't read dc therm limits rc = %d\n", + rc); + return rc; + } + } else { + chg->mmi.dc_thermal_mitigation = NULL; + chg->mmi.dc_thermal_levels = 0; + dev_err(chg->dev, "dc-thermal-mitigation is not set\n"); + } + + rc = of_property_read_u32(node, "qcom,iterm-ma", + &chg->mmi.chrg_iterm); + if (rc) + chg->mmi.chrg_iterm = 150; + + rc = of_property_read_u32(node, "qcom,dc-eb-icl-ma", + &chg->mmi.dc_ebmax_current_ma); + if (rc) + chg->mmi.dc_ebmax_current_ma = 900; + + chg->mmi.dc_ebmax_current_ma_default = chg->mmi.dc_ebmax_current_ma; + + rc = of_property_read_u32(node, "qcom,dc-eb-icl-eff-ma", + &chg->mmi.dc_eff_current_ma); + if (rc) + chg->mmi.dc_eff_current_ma = 500; + + /* read the external battery power supply name */ + rc = of_property_read_string(node, "qcom,eb-batt-psy-name", + &chg->mmi.eb_batt_psy_name); + if (rc) + chg->mmi.eb_batt_psy_name = "gb_battery"; + + /* read the external power control power supply name */ + rc = of_property_read_string(node, "qcom,eb-pwr-psy-name", + &chg->mmi.eb_pwr_psy_name); + if (rc) + chg->mmi.eb_pwr_psy_name = "gb_ptp"; + + /* read the external fuel gauge power supply name */ + rc = of_property_read_string(node, "qcom,extrn-fg-psy-name", + &chg->mmi.extrn_fg_psy_name); + if (rc) + chg->mmi.extrn_fg_psy_name = NULL; + + chg->mmi.vl_ebsrc = MICRO_9V; + chg->mmi.check_ebsrc_vl = false; + + chg->mmi.enable_charging_limit = + of_property_read_bool(node, "qcom,enable-charging-limit"); + + rc = of_property_read_u32(node, "qcom,upper-limit-capacity", + &chg->mmi.upper_limit_capacity); + if (rc) + chg->mmi.upper_limit_capacity = 100; + + rc = of_property_read_u32(node, "qcom,lower-limit-capacity", + &chg->mmi.lower_limit_capacity); + if (rc) + chg->mmi.lower_limit_capacity = 0; + + rc = of_property_read_u32(node, "qcom,vfloat-comp-uv", + &chg->mmi.vfloat_comp_mv); + if (rc) + chg->mmi.vfloat_comp_mv = 0; + chg->mmi.vfloat_comp_mv /= 1000; + + return rc; +} + +static enum alarmtimer_restart mmi_heartbeat_alarm_cb(struct alarm *alarm, + ktime_t now) +{ + struct smb_charger *chip = container_of(alarm, struct smb_charger, + mmi.heartbeat_alarm); + + smblib_dbg(chip, PR_MOTO, "SMB: HB alarm fired\n"); + + __pm_stay_awake(&chip->mmi.smblib_mmi_hb_wake_source); + cancel_delayed_work(&chip->mmi.heartbeat_work); + /* Delay by 500 ms to allow devices to resume. */ + schedule_delayed_work(&chip->mmi.heartbeat_work, + msecs_to_jiffies(500)); + + return ALARMTIMER_NORESTART; +} + +void mmi_init(struct smb_charger *chg) +{ + int rc; + + if (!chg) + return; + + chg->ipc_log = ipc_log_context_create(QPNP_LOG_PAGES, + "charger", 0); + if (chg->ipc_log == NULL) + pr_err("%s: failed to create charger IPC log\n", + __func__); + else + smblib_dbg(chg, PR_MISC, + "IPC logging is enabled for charger\n"); + + chg->ipc_log_reg = ipc_log_context_create(QPNP_LOG_PAGES, + "charger_reg", 0); + if (chg->ipc_log_reg == NULL) + pr_err("%s: failed to create register IPC log\n", + __func__); + else + smblib_dbg(chg, PR_MISC, + "IPC logging is enabled for charger\n"); + + mmi_chip = chg; + chg->voltage_min_uv = MICRO_5V; + chg->voltage_max_uv = MICRO_9V; + chg->mmi.factory_mode = mmi_factory_check(); + + chg->mmi.is_factory_image = false; + chg->mmi.charging_limit_modes = CHARGING_LIMIT_UNKNOWN; + chg->mmi.charger_rate = POWER_SUPPLY_CHARGE_RATE_NONE; + + INIT_DELAYED_WORK(&chg->mmi.heartbeat_work, mmi_heartbeat_work); + wakeup_source_init(&chg->mmi.smblib_mmi_hb_wake_source, + "smblib_mmi_hb_wake"); + alarm_init(&chg->mmi.heartbeat_alarm, ALARM_BOOTTIME, + mmi_heartbeat_alarm_cb); + + rc = parse_mmi_dt(chg); + if (rc < 0) + smblib_err(chg, "Error getting mmi dt items\n"); + + parse_mmi_dt_gpio(chg); + + chg->mmi.smb_reboot.notifier_call = smbchg_reboot; + chg->mmi.smb_reboot.next = NULL; + chg->mmi.smb_reboot.priority = 1; + rc = register_reboot_notifier(&chg->mmi.smb_reboot); + if (rc) + smblib_err(chg, "SMB register for reboot failed\n"); + + rc = device_create_file(chg->dev, + &dev_attr_force_demo_mode); + if (rc) { + smblib_err(chg, "couldn't create force_demo_mode\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_force_max_chrg_temp); + if (rc) { + smblib_err(chg, "couldn't create force_max_chrg_temp\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_factory_image_mode); + if (rc) + smblib_err(chg, "couldn't create factory_image_mode\n"); + + rc = device_create_file(chg->dev, + &dev_attr_factory_charge_upper); + if (rc) + smblib_err(chg, "couldn't create factory_charge_upper\n"); + + if (chg->mmi.factory_mode) { + mmi_chip = chg; + smblib_err(chg, "Entering Factory Mode SMB!\n"); + + rc = device_create_file(chg->dev, + &dev_attr_force_chg_usb_suspend); + if (rc) { + smblib_err(chg, + "couldn't create force_chg_usb_suspend\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_force_chg_fail_clear); + if (rc) { + smblib_err(chg, + "couldn't create force_chg_fail_clear\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_force_chg_auto_enable); + if (rc) { + smblib_err(chg, + "couldn't create force_chg_auto_enable\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_force_chg_ibatt); + if (rc) { + smblib_err(chg, "couldn't create force_chg_ibatt\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_force_chg_iusb); + if (rc) { + smblib_err(chg, "couldn't create force_chg_iusb\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_force_chg_itrick); + if (rc) { + smblib_err(chg, "couldn't create force_chg_itrick\n"); + } + } + + /* reconfigure allowed voltage for HVDCP */ + rc = smblib_set_adapter_allowance(chg, + USBIN_ADAPTER_ALLOW_5V_TO_9V); + + /* Set DCIN adapter allowance */ + rc = smblib_masked_write(chg, DCIN_ADAPTER_ALLOW_CFG_REG, + DCIN_ADAPTER_ALLOW_MASK, + DCIN_ADAPTER_ALLOW_5V_TO_9V); + if (rc) + smblib_err(chg, "couldn't set DCIN adapter allowance\n"); + + /* Turn Jeita OFF */ + rc = smblib_masked_write(chg, JEITA_EN_CFG_REG, + 0x3F, + 0x00); + if (rc) + smblib_err(chg, "couldn't set JEITA CFG\n"); + + /* Register the notifier for the psy updates*/ + chg->mmi.mmi_psy_notifier.notifier_call = mmi_psy_notifier_call; + rc = power_supply_reg_notifier(&chg->mmi.mmi_psy_notifier); + if (rc) + smblib_err(chg, "failed to reg notifier: %d\n", rc); + + chg->mmi.init_done = true; +} + +void mmi_deinit(struct smb_charger *chg) +{ + if (!chg || !chg->mmi.init_done) + return; + + device_remove_file(chg->dev, + &dev_attr_force_demo_mode); + device_remove_file(chg->dev, + &dev_attr_force_max_chrg_temp); + + if (chg->mmi.factory_mode) { + device_remove_file(chg->dev, + &dev_attr_force_chg_usb_suspend); + device_remove_file(chg->dev, + &dev_attr_force_chg_fail_clear); + device_remove_file(chg->dev, + &dev_attr_force_chg_auto_enable); + device_remove_file(chg->dev, + &dev_attr_force_chg_ibatt); + device_remove_file(chg->dev, + &dev_attr_force_chg_iusb); + device_remove_file(chg->dev, + &dev_attr_force_chg_itrick); + } + wakeup_source_trash(&chg->mmi.smblib_mmi_hb_wake_source); + power_supply_unreg_notifier(&chg->mmi.mmi_psy_notifier); +} diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h index 6294edca9efa..2638296835e0 100644 --- a/drivers/power/supply/qcom/smb5-lib.h +++ b/drivers/power/supply/qcom/smb5-lib.h @@ -5,18 +5,24 @@ #ifndef __SMB5_CHARGER_H #define __SMB5_CHARGER_H +#include #include #include #include #include #include #include +#include #include #include #include #include #include "storm-watch.h" #include "battery.h" +#include +#include +#include +#include enum print_reason { PR_INTERRUPT = BIT(0), @@ -25,6 +31,7 @@ enum print_reason { PR_PARALLEL = BIT(3), PR_OTG = BIT(4), PR_WLS = BIT(5), + PR_MOTO = BIT(7), }; #define DEFAULT_VOTER "DEFAULT_VOTER" @@ -47,6 +54,7 @@ enum print_reason { #define CTM_VOTER "CTM_VOTER" #define SW_QC3_VOTER "SW_QC3_VOTER" #define AICL_RERUN_VOTER "AICL_RERUN_VOTER" +#define ICL_LIMIT_VOTER "ICL_LIMIT_VOTER" #define SW_ICL_MAX_VOTER "SW_ICL_MAX_VOTER" #define PL_QNOVO_VOTER "PL_QNOVO_VOTER" #define QNOVO_VOTER "QNOVO_VOTER" @@ -85,6 +93,11 @@ enum print_reason { #define BOOST_BACK_STORM_COUNT 3 #define WEAK_CHG_STORM_COUNT 8 +#define HEARTBEAT_VOTER "HEARTBEAT_VOTER" +#define EB_VOTER "EB_VOTER" +#define WIRELESS_VOTER "WIRELESS_VOTER" +#define DEMO_VOTER "DEMO_VOTER" +#define MMI_VOTER "MMI_VOTER" #define VBAT_TO_VRAW_ADC(v) div_u64((u64)v * 1000000UL, 194637UL) @@ -259,6 +272,10 @@ struct smb_irq_info { static const unsigned int smblib_extcon_cable[] = { EXTCON_USB, EXTCON_USB_HOST, +#ifdef CONFIG_MODS_NEW_SW_ARCH + EXTCON_USB_CC, + EXTCON_USB_SPEED, +#endif EXTCON_NONE, }; @@ -371,6 +388,123 @@ struct smb_iio { struct iio_channel *die_temp_chan; struct iio_channel *skin_temp_chan; struct iio_channel *smb_temp_chan; + struct iio_channel *dcin_v_chan; +}; + +struct mmi_temp_zone { + int temp_c; + int norm_mv; + int fcc_max_ma; + int fcc_norm_ma; +}; + +#define MAX_NUM_STEPS 10 +enum mmi_temp_zones { + ZONE_FIRST = 0, + /* states 0-9 are reserved for zones */ + ZONE_LAST = MAX_NUM_STEPS + ZONE_FIRST - 1, + ZONE_HOT, + ZONE_COLD, + ZONE_NONE = 0xFF, +}; + +enum mmi_chrg_step { + STEP_MAX, + STEP_NORM, + STEP_EB, + STEP_FULL, + STEP_FLOAT, + STEP_DEMO, + STEP_STOP, + STEP_NONE = 0xFF, +}; + +enum ebchg_state { + EB_DISCONN = POWER_SUPPLY_EXTERN_STATE_DIS, + EB_SINK = POWER_SUPPLY_EXTERN_STATE_SINK, + EB_SRC = POWER_SUPPLY_EXTERN_STATE_SRC, + EB_OFF = POWER_SUPPLY_EXTERN_STATE_OFF, +}; + +enum charging_limit_modes { + CHARGING_LIMIT_OFF, + CHARGING_LIMIT_RUN, + CHARGING_LIMIT_UNKNOWN, +}; + +enum turbo_ebsrc { + TURBO_EBSRC_UNKNOWN, + TURBO_EBSRC_NOT_SUPPORTED, + TURBO_EBSRC_VALID, +}; + +struct mmi_params { + bool factory_mode; + int demo_mode; + struct gpio ebchg_gpio; + struct notifier_block smb_reboot; + /* thermal mitigation */ + int dc_system_temp_level; + int dc_thermal_levels; + int *dc_thermal_mitigation; + int usb_system_temp_level; + int usb_thermal_levels; + int *usb_thermal_mitigation; + bool factory_kill_armed; + + /* Charge Profile */ + int num_temp_zones; + struct mmi_temp_zone *temp_zones; + enum mmi_temp_zones pres_temp_zone; + enum mmi_chrg_step pres_chrg_step; + int chrg_taper_cnt; + int dc_ebmax_current_ma; + int dc_ebmax_current_ma_default; + int dc_eff_current_ma; + /* external battery params */ + const char *eb_batt_psy_name; + const char *eb_pwr_psy_name; + const char *extrn_fg_psy_name; + enum ebchg_state ebchg_state; + bool force_eb_chrg; + int update_eb_params; + int charger_debounce_cnt; + int cl_ebchg; + int cl_ebsrc; + int vl_ebsrc; + int vo_ebsrc; + int vi_ebsrc; + bool eb_rechrg; + bool usbeb_present; + bool wls_present; + int temp_state; + int chrg_iterm; + atomic_t hb_ready; + struct alarm heartbeat_alarm; + struct delayed_work heartbeat_work; + struct power_supply *wls_psy; + struct power_supply *usbeb_psy; + struct pinctrl *smb_pinctrl; + struct wakeup_source smblib_mmi_hb_wake_source; + bool apsd_done; + int charger_rate; + bool hvdcp3_con; + struct notifier_block mmi_psy_notifier; + bool init_done; + int vbus_inc_cnt; + bool enable_charging_limit; + bool is_factory_image; + enum charging_limit_modes charging_limit_modes; + int upper_limit_capacity; + int lower_limit_capacity; + int base_fv_mv; + int vfloat_comp_mv; + enum turbo_ebsrc turbo_pwr_ebsrc; + bool check_ebsrc_vl; + int batt_health; + int max_chrg_temp; + int is_otg_enable; + bool force_chg_suspend; }; struct smb_charger { @@ -398,7 +532,7 @@ struct smb_charger { struct mutex adc_lock; struct mutex dpdm_lock; struct mutex typec_lock; - + struct mutex otg_lock; /* power supplies */ struct power_supply *batt_psy; struct power_supply *usb_psy; @@ -437,6 +571,7 @@ struct smb_charger { struct votable *fcc_main_votable; struct votable *fv_votable; struct votable *usb_icl_votable; + struct votable *dc_icl_votable; struct votable *awake_votable; struct votable *pl_disable_votable; struct votable *chg_disable_votable; @@ -509,6 +644,10 @@ struct smb_charger { int dcp_icl_ua; int fake_capacity; int fake_batt_status; + bool extrn_fg; + bool dc_unsupported; + bool wireless_unsupported; + bool usbeb_unsupported; bool step_chg_enabled; bool sw_jeita_enabled; bool jeita_arb_enable; @@ -617,6 +756,19 @@ struct smb_charger { int dcin_uv_count; ktime_t dcin_uv_last_time; int last_wls_vout; + /* mmi based params */ + /* Place at end of struct smb_charger as it grows */ + struct mmi_params mmi; + void *ipc_log; + void *ipc_log_reg; + struct usbpd *pd; + int pd_contract_uv; + struct delayed_work pd_contract_work; + bool external_vbus; + bool support_mods; + bool suspended; + u32 source_current_ma; + bool reverse_boost; }; int smblib_read(struct smb_charger *chg, u16 addr, u8 *val); @@ -657,6 +809,7 @@ int smblib_vconn_regulator_is_enabled(struct regulator_dev *rdev); irqreturn_t default_irq_handler(int irq, void *data); irqreturn_t smb_en_irq_handler(int irq, void *data); +irqreturn_t smblib_handle_usbin_collapse(int irq, void *data); irqreturn_t chg_state_change_irq_handler(int irq, void *data); irqreturn_t batt_temp_changed_irq_handler(int irq, void *data); irqreturn_t batt_psy_changed_irq_handler(int irq, void *data); @@ -676,12 +829,17 @@ irqreturn_t typec_or_rid_detection_change_irq_handler(int irq, void *data); irqreturn_t temp_change_irq_handler(int irq, void *data); irqreturn_t usbin_ov_irq_handler(int irq, void *data); irqreturn_t sdam_sts_change_irq_handler(int irq, void *data); + +irqreturn_t dcin_uv_irq_handler(int irq, void *data); + int smblib_get_prop_input_suspend(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_batt_present(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_batt_capacity(struct smb_charger *chg, union power_supply_propval *val); +int smblib_get_prop_batt_age(struct smb_charger *chg, + union power_supply_propval *val); int smblib_get_prop_batt_status(struct smb_charger *chg, union power_supply_propval *val); int smblib_get_prop_batt_charge_type(struct smb_charger *chg, @@ -841,4 +999,29 @@ int smblib_get_qc3_main_icl_offset(struct smb_charger *chg, int *offset_ua); int smblib_init(struct smb_charger *chg); int smblib_deinit(struct smb_charger *chg); + +int smblib_get_prop_dc_system_temp_level(struct smb_charger *chg, + union power_supply_propval *val); +int smblib_set_prop_dc_system_temp_level(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_get_prop_usb_system_temp_level(struct smb_charger *chg, + union power_supply_propval *val); +int smblib_set_prop_usb_system_temp_level(struct smb_charger *chg, + const union power_supply_propval *val); +void mmi_init(struct smb_charger *chg); +void mmi_deinit(struct smb_charger *chg); +void mmi_chrg_rate_check(struct smb_charger *chip); + +#define HEARTBEAT_EB_MS 1000 +extern struct smb_charger *mmi_chip; +extern int eb_rechrg_start_soc; +extern int eb_rechrg_stop_soc; +extern int eb_attach_start_soc; +extern int eb_attach_stop_soc; +extern int eb_low_start_soc; +extern int eb_low_stop_soc; +extern int eb_on_sw; + +#define DEFAULT_SOURCE_CURRENT_MA 1500 + #endif /* __SMB5_CHARGER_H */ diff --git a/drivers/power/supply/qcom/smb5-reg.h b/drivers/power/supply/qcom/smb5-reg.h index 311e810c31c0..340bc75bf8c8 100644 --- a/drivers/power/supply/qcom/smb5-reg.h +++ b/drivers/power/supply/qcom/smb5-reg.h @@ -70,6 +70,9 @@ enum { #define SOC_BASED_RECHG_BIT GENMASK(2, 1) #define CHARGER_INHIBIT_BIT BIT(0) +#define CHGR_PRE_CHARGE_CURRENT_CFG_REG (CHGR_BASE + 0x60) +#define CHGR_PRE_CHARGE_CURRENT_SETTING_MASK GENMASK(2, 0) + #define CHGR_FAST_CHARGE_CURRENT_CFG_REG (CHGR_BASE + 0x61) #define CHGR_ADC_ITERM_UP_THD_MSB_REG (CHGR_BASE + 0x67) @@ -275,6 +278,19 @@ enum { HVDCP_PULSE_COUNT_MAX_QC2_INVALID = 0xC0 }; +#define USBIN_ADAPTER_ALLOW_CFG_REG (USBIN_BASE + 0x60) +enum { + USBIN_ADAPTER_ALLOW_5V = 0, + USBIN_ADAPTER_ALLOW_9V = 2, + USBIN_ADAPTER_ALLOW_5V_OR_9V = 3, + USBIN_ADAPTER_ALLOW_12V = 4, + USBIN_ADAPTER_ALLOW_5V_OR_12V = 5, + USBIN_ADAPTER_ALLOW_9V_TO_12V = 6, + USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V = 7, + USBIN_ADAPTER_ALLOW_5V_TO_9V = 8, + USBIN_ADAPTER_ALLOW_5V_TO_12V = 12, +}; + #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) @@ -292,6 +308,8 @@ enum { #define ICL_OVERRIDE_AFTER_APSD_BIT BIT(4) #define USBIN_AICL_STEP_TIMING_SEL_MASK GENMASK(3, 2) #define USBIN_IN_COLLAPSE_GF_SEL_MASK GENMASK(1, 0) +#define USBIN_IN_COLLAPSE_GF_30MS 0x2 +#define USBIN_IN_COLLAPSE_GF_30US 0x3 #define USBIN_ICL_OPTIONS_REG (USBIN_BASE + 0x66) #define CFG_USB3P0_SEL_BIT BIT(2) @@ -302,6 +320,7 @@ enum { #define USBIN_AICL_OPTIONS_CFG_REG (USBIN_BASE + 0x80) #define SUSPEND_ON_COLLAPSE_USBIN_BIT BIT(7) +#define USBIN_AICL_HDC_EN_BIT BIT(6) #define USBIN_AICL_PERIODIC_RERUN_EN_BIT BIT(4) #define USBIN_AICL_ADC_EN_BIT BIT(3) #define USBIN_AICL_EN_BIT BIT(2) @@ -329,7 +348,19 @@ enum { #define DCIN_LOAD_CFG_REG (DCIN_BASE + 0x65) #define INPUT_MISS_POLL_EN_BIT BIT(5) - +#define DCIN_ADAPTER_ALLOW_CFG_REG (DCIN_BASE + 0x60) +#define DCIN_ADAPTER_ALLOW_MASK GENMASK(3, 0) +enum { + DCIN_ADAPTER_ALLOW_5V = 0, + DCIN_ADAPTER_ALLOW_9V = 2, + DCIN_ADAPTER_ALLOW_5V_OR_9V = 3, + DCIN_ADAPTER_ALLOW_12V = 4, + DCIN_ADAPTER_ALLOW_5V_OR_12V = 5, + DCIN_ADAPTER_ALLOW_9V_TO_12V = 6, + DCIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V = 7, + DCIN_ADAPTER_ALLOW_5V_TO_9V = 8, + DCIN_ADAPTER_ALLOW_5V_TO_12V = 12, +}; /******************************** * TYPEC Peripheral Registers * ********************************/ diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c index 6dca69d619b4..304a95689002 100644 --- a/drivers/usb/pd/policy_engine.c +++ b/drivers/usb/pd/policy_engine.c @@ -234,6 +234,7 @@ static void *usbpd_ipc_log; #define VDM_BUSY_TIME 50 #define VCONN_ON_TIME 100 #define SINK_TX_TIME 16 +#define APSD_RECHECK_TIME 5000 /* tPSHardReset + tSafe0V */ #define SNK_HARD_RESET_VBUS_OFF_TIME (35 + 650) @@ -305,6 +306,7 @@ static void *usbpd_ipc_log; #define PD_SRC_PDO_FIXED_PEAK_CURR(pdo) (((pdo) >> 20) & 3) #define PD_SRC_PDO_FIXED_VOLTAGE(pdo) (((pdo) >> 10) & 0x3FF) #define PD_SRC_PDO_FIXED_MAX_CURR(pdo) ((pdo) & 0x3FF) +#define PD_SRC_PDO_FIXED_CURRENT_MASK 0x3FF #define PD_SRC_PDO_VAR_BATT_MAX_VOLT(pdo) (((pdo) >> 20) & 0x3FF) #define PD_SRC_PDO_VAR_BATT_MIN_VOLT(pdo) (((pdo) >> 10) & 0x3FF) @@ -353,7 +355,7 @@ static void *usbpd_ipc_log; #define PD_MIN_SINK_CURRENT 900 -static const u32 default_src_caps[] = { 0x36019096 }; /* VSafe5V @ 1.5A */ +static u32 default_src_caps[] = { 0x36019096 }; /* VSafe5V @ 1.5A */ static const u32 default_snk_caps[] = { 0x2601912C }; /* VSafe5V @ 3A */ struct vdm_tx { @@ -493,6 +495,10 @@ static const unsigned int usbpd_extcon_cable[] = { EXTCON_USB, EXTCON_USB_HOST, EXTCON_DISP_DP, +#ifdef CONFIG_MODS_NEW_SW_ARCH + EXTCON_USB_CC, + EXTCON_USB_SPEED, +#endif EXTCON_NONE, }; @@ -550,6 +556,12 @@ static inline void start_usb_host(struct usbpd *pd, bool ss) union extcon_property_value val; int ret = 0; +#ifdef CONFIG_MODS_NEW_SW_ARCH + extcon_set_cable_state_(pd->extcon, EXTCON_USB_CC, + cc == ORIENTATION_CC2); + extcon_set_cable_state_(pd->extcon, EXTCON_USB_SPEED, ss); +#endif + val.intval = (cc == ORIENTATION_CC2); extcon_set_property(pd->extcon, EXTCON_USB_HOST, EXTCON_PROP_USB_TYPEC_POLARITY, val); @@ -578,6 +590,12 @@ static inline void start_usb_peripheral(struct usbpd *pd) enum plug_orientation cc = usbpd_get_plug_orientation(pd); union extcon_property_value val; +#ifdef CONFIG_MODS_NEW_SW_ARCH + extcon_set_cable_state_(pd->extcon, EXTCON_USB_CC, + cc == ORIENTATION_CC2); + extcon_set_cable_state_(pd->extcon, EXTCON_USB_SPEED, 1); +#endif + val.intval = (cc == ORIENTATION_CC2); extcon_set_property(pd->extcon, EXTCON_USB, EXTCON_PROP_USB_TYPEC_POLARITY, val); @@ -887,10 +905,38 @@ static int pd_select_pdo(struct usbpd *pd, int pdo_pos, int uv, int ua) pd->requested_current = curr; pd->requested_pdo = pdo_pos; + usbpd_warn(&pd->dev, "select_pdo: PDO:%d, %d uV, %d uA, type %d\n", + pdo_pos, uv, ua, type); return 0; } +static int pd_get_pdo(struct usbpd *pd, int pdo_pos, int *uv_max, int *uv_min, int *ua) +{ + u8 type; + u32 pdo = pd->received_pdos[pdo_pos - 1]; + + if (0 == pdo) + return -ENOTSUPP; + + type = PD_SRC_PDO_TYPE(pdo); + if (type == PD_SRC_PDO_TYPE_FIXED) { + *ua = PD_SRC_PDO_FIXED_MAX_CURR(pdo) * 10; + *uv_max = *uv_min = PD_SRC_PDO_FIXED_VOLTAGE(pdo) * 50 * 1000; + } else if (type == PD_SRC_PDO_TYPE_AUGMENTED) { + *uv_max = PD_APDO_MAX_VOLT(pdo) * 100000; + *uv_min = PD_APDO_MIN_VOLT(pdo) * 100000; + *ua = PD_APDO_MAX_CURR(pdo) * 50000; + } else { + usbpd_err(&pd->dev, "Only Fixed or Programmable PDOs supported\n"); + return -ENOTSUPP; + } + + usbpd_warn(&pd->dev, "pdo_pos = %d, type= %d, uv_max = %d, uv_min= %d, *ua = %d\n", + pdo_pos, type, *uv_max, *uv_min, *ua); + return 0; +} + static int pd_eval_src_caps(struct usbpd *pd) { int i; @@ -1242,6 +1288,7 @@ static enum hrtimer_restart pd_timeout(struct hrtimer *timer) { struct usbpd *pd = container_of(timer, struct usbpd, timer); + usbpd_dbg(&pd->dev, "timeout"); queue_work(pd->wq, &pd->sm_work); return HRTIMER_NORESTART; @@ -4269,9 +4316,228 @@ static ssize_t pdo_n_show(struct device *dev, struct device_attribute *attr, return -EINVAL; } +int usbpd_get_current_dr(struct usbpd *pd) +{ + if (!pd) + return -EINVAL; + + return (int)pd->current_dr; +} +EXPORT_SYMBOL(usbpd_get_current_dr); + +int usbpd_select_pdo_match(struct usbpd *pd) +{ + int uv_diff = 0, max_uv_diff = 0, pdo = 0; + int pdo_max_uv = 0, pdo_min_uv = 0, pdo_ua = 0; + int uv_in, i; + int ret; + union power_supply_propval val; + + if (!pd) + return -EINVAL; + + mutex_lock(&pd->swap_lock); + + if (pd->current_pr == PR_SRC) { + usbpd_err(&pd->dev, "select_pdo:not support in source mode\n"); + ret = -ENOTSUPP; + goto out; + } + + /* Only allowed if we are already in explicit sink contract */ + if (pd->current_state != PE_SNK_READY || !is_sink_tx_ok(pd)) { + usbpd_err(&pd->dev, "select_pdo: Cannot select new PDO yet\n"); + ret = -EBUSY; + goto out; + } + + /* Check the Max Input Voltage from USBPSY */ + power_supply_get_property(pd->usb_psy, + POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, &val); + uv_in = val.intval; + /* start with 5 V */ + max_uv_diff = 5000000; + for (i = 1; i < 8; i++) { + if (!pd_get_pdo(pd, i, &pdo_max_uv, &pdo_min_uv, &pdo_ua)) { + if (pdo_max_uv <= uv_in) { + uv_diff = uv_in - pdo_max_uv; + if (uv_diff < max_uv_diff) { + max_uv_diff = uv_diff; + pdo = i; + } + } + } + } + + if (pdo < 1 || pdo > 7) { + usbpd_err(&pd->dev, "select_pdo: invalid PDO:%d\n", pdo); + ret = -EINVAL; + goto out; + } + + pd_get_pdo(pd, pdo, &pdo_max_uv, &pdo_min_uv, &pdo_ua); + ret = pd_select_pdo(pd, pdo, pdo_max_uv, pdo_ua); + if (ret) + goto out; + else + ret = pdo_max_uv; + + reinit_completion(&pd->is_ready); + pd->send_request = true; + kick_sm(pd, 0); + + /* wait for operation to complete */ + if (!wait_for_completion_timeout(&pd->is_ready, + msecs_to_jiffies(1000))) { + usbpd_err(&pd->dev, "select_pdo: request timed out\n"); + ret = -ETIMEDOUT; + goto out; + } + + /* determine if request was accepted/rejected */ + if (pd->selected_pdo != pd->requested_pdo || + pd->current_voltage != pd->requested_voltage) { + usbpd_err(&pd->dev, "select_pdo: request rejected\n"); + ret = -EINVAL; + } + +out: + pd->send_request = false; + mutex_unlock(&pd->swap_lock); + return ret; +} +EXPORT_SYMBOL(usbpd_select_pdo_match); + +int usbpd_get_pdo_info(struct usbpd *pd, struct usbpd_pdo_info *pdo_info) +{ + int pdo = 0; + int i; + int ret = 0; + + if (!pd || !pdo_info) + return -EINVAL; + + mutex_lock(&pd->swap_lock); + + if (pd->current_pr == PR_SRC) { + usbpd_err(&pd->dev, "select_pdo:not support in source mode\n"); + ret = -ENOTSUPP; + goto out; + } + usbpd_err(&pd->dev, "select pdo info ,pd->current_state %d\n", pd->current_state); + + if (!is_sink_tx_ok(pd)) + usbpd_err(&pd->dev, "select pdo info , !is_sink_tx_ok\n"); + + /* Only allowed if we are already in explicit sink contract */ + if (pd->current_state != PE_SNK_READY || !is_sink_tx_ok(pd)) { + usbpd_err(&pd->dev, "select_pdo_info: Cannot select new PDO yet\n"); + ret = -EBUSY; + goto out; + } + + for (i = 0; i < 7; i++) { + pdo = pd->received_pdos[i]; + if (pdo == 0) + break; + + pdo_info[i].pdo_pos = i + 1; + pdo_info[i].type = PD_SRC_PDO_TYPE(pdo); + if (pdo_info[i].type == PD_SRC_PDO_TYPE_FIXED) { + pdo_info[i].ua = PD_SRC_PDO_FIXED_MAX_CURR(pdo) * 10; + pdo_info[i].uv_max = PD_SRC_PDO_FIXED_VOLTAGE(pdo) * 50 * 1000; + pdo_info[i].uv_min = PD_SRC_PDO_FIXED_VOLTAGE(pdo) * 50 * 1000; + + printk(KERN_INFO "pdo %d, Fixed supply\n" + "Voltage:%d (uV)\n" + "Max Current:%d (uA)\n", + pdo_info[i].pdo_pos, + pdo_info[i].uv_max, + pdo_info[i].ua); + + } else if (pdo_info[i].type == PD_SRC_PDO_TYPE_AUGMENTED) { + pdo_info[i].uv_max = PD_APDO_MAX_VOLT(pdo) * 100000; + pdo_info[i].uv_min = PD_APDO_MIN_VOLT(pdo) * 100000; + pdo_info[i].ua = PD_APDO_MAX_CURR(pdo) * 50000; + + printk(KERN_INFO "pdo %d, Programmable Power supply\n" + "Voltage:%d (uV)\n" + "Max Current:%d (uA)\n", + pdo_info[i].pdo_pos, + pdo_info[i].uv_max, + pdo_info[i].ua); + + } else + usbpd_err(&pd->dev, "Only get Fixed or Programmable PDOs supported\n"); + } +out: + mutex_unlock(&pd->swap_lock); + return ret; +} +EXPORT_SYMBOL(usbpd_get_pdo_info); + +int usbpd_select_pdo(struct usbpd *pd, int pdo_pos, int uv, int ua) +{ + int ret; + + if (!pd) + return -EINVAL; + + mutex_lock(&pd->swap_lock); + + if (pd->current_pr == PR_SRC) { + usbpd_err(&pd->dev, "select_pdo:not support in source mode\n"); + ret = -ENOTSUPP; + goto out; + } + + /* Only allowed if we are already in explicit sink contract */ + if (pd->current_state != PE_SNK_READY || !is_sink_tx_ok(pd)) { + usbpd_err(&pd->dev, "select_pdo: Cannot select new PDO yet\n"); + ret = -EBUSY; + goto out; + } + + if (pdo_pos < 1 || pdo_pos > 7) { + usbpd_err(&pd->dev, "select_pdo: invalid PDO:%d\n", pdo_pos); + ret = -EINVAL; + goto out; + } + + ret = pd_select_pdo(pd, pdo_pos, uv, ua); + if (ret) + goto out; + + reinit_completion(&pd->is_ready); + pd->send_request = true; + kick_sm(pd, 0); + + /* wait for operation to complete */ + if (!wait_for_completion_timeout(&pd->is_ready, + msecs_to_jiffies(1000))) { + usbpd_err(&pd->dev, "select_pdo: request timed out\n"); + ret = -ETIMEDOUT; + goto out; + } + + /* determine if request was accepted/rejected */ + if (pd->selected_pdo != pd->requested_pdo || + pd->current_voltage != pd->requested_voltage) { + usbpd_err(&pd->dev, "select_pdo: request rejected\n"); + ret = -EINVAL; + } + +out: + pd->send_request = false; + mutex_unlock(&pd->swap_lock); + return ret; +} +EXPORT_SYMBOL(usbpd_select_pdo); + static ssize_t select_pdo_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { +#ifdef QCOM_BASE struct usbpd *pd = dev_get_drvdata(dev); int src_cap_id; int pdo, uv = 0, ua = 0; @@ -4333,6 +4599,9 @@ out: pd->send_request = false; mutex_unlock(&pd->swap_lock); return ret ? ret : size; +#else + return size; +#endif } static ssize_t select_pdo_show(struct device *dev, @@ -4458,6 +4727,7 @@ out: static ssize_t get_src_cap_ext_show(struct device *dev, struct device_attribute *attr, char *buf) { +#ifdef QCOM_BASE int i, ret, len = 0; struct usbpd *pd = dev_get_drvdata(dev); @@ -4475,6 +4745,11 @@ static ssize_t get_src_cap_ext_show(struct device *dev, len += scnprintf(buf + len, PAGE_SIZE - len, "\n"); return len; +#else + struct usbpd *pd = dev_get_drvdata(dev); + usbpd_warn(&pd->dev, "%s don't get src cap ext\n", __func__); + return -EINVAL; +#endif } static DEVICE_ATTR_RO(get_src_cap_ext); @@ -4707,6 +4982,7 @@ struct usbpd *usbpd_create(struct device *parent) int ret; struct usbpd *pd; union power_supply_propval val = {0}; + u32 source_current = 0; pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) @@ -4837,6 +5113,20 @@ struct usbpd *usbpd_create(struct device *parent) if (device_property_read_bool(parent, "qcom,pd-20-source-only")) pd->pd20_source_only = true; + device_property_read_u32(parent, + "qcom,source-current", + &source_current); + + if (source_current) { + usbpd_dbg(&pd->dev, "Override default src current with %d\n", + source_current); + source_current = (source_current / 10) & + PD_SRC_PDO_FIXED_CURRENT_MASK; + default_src_caps[0] = (*default_src_caps & + (~PD_SRC_PDO_FIXED_CURRENT_MASK)) | + source_current; + } + /* * Register a Type-C class instance (/sys/class/typec/portX). * Note this is different than the /sys/class/usbpd/ created above. diff --git a/drivers/usb/pd/qpnp-pdphy.c b/drivers/usb/pd/qpnp-pdphy.c index fd798920676c..356d01939e98 100644 --- a/drivers/usb/pd/qpnp-pdphy.c +++ b/drivers/usb/pd/qpnp-pdphy.c @@ -74,7 +74,7 @@ #define PD_MSG_HDR_REV(hdr) (((hdr) >> 6) & 3) /* timers */ -#define RECEIVER_RESPONSE_TIME 15 /* tReceiverResponse */ +#define RECEIVER_RESPONSE_TIME 150 /* tReceiverResponse */ #define HARD_RESET_COMPLETE_TIME 5 /* tHardResetComplete */ struct usb_pdphy { diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 72fe67bf5efc..179b9c2f2631 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -191,6 +191,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_CHARGE_EMPTY, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_AVG, + POWER_SUPPLY_PROP_CHARGE_RATE, POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, @@ -256,6 +257,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_COLD_TEMP, POWER_SUPPLY_PROP_HOT_TEMP, POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, + POWER_SUPPLY_PROP_NUM_SYSTEM_TEMP_LEVELS, POWER_SUPPLY_PROP_RESISTANCE, POWER_SUPPLY_PROP_RESISTANCE_CAPACITIVE, POWER_SUPPLY_PROP_RESISTANCE_ID, /* in Ohms */ @@ -346,6 +348,7 @@ enum power_supply_property { POWER_SUPPLY_PROP_COMP_CLAMP_LEVEL, POWER_SUPPLY_PROP_ADAPTER_CC_MODE, POWER_SUPPLY_PROP_SKIN_HEALTH, + POWER_SUPPLY_PROP_VBUS_CONFLICT, POWER_SUPPLY_PROP_AICL_DONE, POWER_SUPPLY_PROP_VOLTAGE_STEP, POWER_SUPPLY_PROP_APSD_RERUN, @@ -366,6 +369,21 @@ enum power_supply_property { POWER_SUPPLY_PROP_CC_TOGGLE_ENABLE, POWER_SUPPLY_PROP_FG_TYPE, POWER_SUPPLY_PROP_CHARGER_STATUS, + POWER_SUPPLY_PROP_PTP_INTERNAL_SEND, + POWER_SUPPLY_PROP_PTP_INTERNAL_RECEIVE, + POWER_SUPPLY_PROP_PTP_EXTERNAL, + POWER_SUPPLY_PROP_PTP_CURRENT_FLOW, + POWER_SUPPLY_PROP_PTP_MAX_INPUT_CURRENT, + POWER_SUPPLY_PROP_PTP_MAX_OUTPUT_CURRENT, + POWER_SUPPLY_PROP_PTP_EXTERNAL_PRESENT, + POWER_SUPPLY_PROP_PTP_POWER_REQUIRED, + POWER_SUPPLY_PROP_PTP_POWER_AVAILABLE, + POWER_SUPPLY_PROP_PTP_POWER_SOURCE, + POWER_SUPPLY_PROP_PTP_MAX_OUTPUT_VOLTAGE, + POWER_SUPPLY_PROP_PTP_OUTPUT_VOLTAGE, + POWER_SUPPLY_PROP_PTP_MAX_INPUT_VOLTAGE, + POWER_SUPPLY_PROP_PTP_INPUT_VOLTAGE, + POWER_SUPPLY_PROP_AGE, /* Local extensions of type int64_t */ POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT, /* Properties of type `const char *' */ @@ -405,6 +423,7 @@ enum power_supply_type { POWER_SUPPLY_TYPE_UFP, /* Type-C UFP */ POWER_SUPPLY_TYPE_DFP, /* Type-C DFP */ POWER_SUPPLY_TYPE_CHARGE_PUMP, /* Charge Pump */ + POWER_SUPPLY_TYPE_PTP, /* Greybus power transfer protocol */ }; enum power_supply_usb_type { @@ -453,6 +472,8 @@ enum power_supply_typec_power_role { enum power_supply_notifier_events { PSY_EVENT_PROP_CHANGED, + PSY_EVENT_PROP_ADDED, + PSY_EVENT_PROP_REMOVED, }; union power_supply_propval { @@ -461,6 +482,81 @@ union power_supply_propval { int64_t int64val; }; +enum { + POWER_SUPPLY_CHARGE_RATE_NONE = 0, + POWER_SUPPLY_CHARGE_RATE_NORMAL, + POWER_SUPPLY_CHARGE_RATE_WEAK, + POWER_SUPPLY_CHARGE_RATE_TURBO, + POWER_SUPPLY_CHARGE_RATE_HYPER, +}; + +enum { + POWER_SUPPLY_EXTERN_STATE_DIS = 0, + POWER_SUPPLY_EXTERN_STATE_SINK, + POWER_SUPPLY_EXTERN_STATE_SRC, + POWER_SUPPLY_EXTERN_STATE_OFF, +}; + +enum { + POWER_SUPPLY_PTP_INT_SND_UNKNOWN = 0, + POWER_SUPPLY_PTP_INT_SND_NEVER, + POWER_SUPPLY_PTP_INT_SND_SUPPLEMENTAL, + POWER_SUPPLY_PTP_INT_SND_LOW_BATT_SAVER, +}; + +enum { + POWER_SUPPLY_PTP_INT_RCV_UNKNOWN = 0, + POWER_SUPPLY_PTP_INT_RCV_NEVER, + POWER_SUPPLY_PTP_INT_RCV_FIRST, + POWER_SUPPLY_PTP_INT_RCV_SECOND, + POWER_SUPPLY_PTP_INT_RCV_PARALLEL, +}; + +enum { + POWER_SUPPLY_PTP_EXT_SUPPORT_UNKNOWN = 0, + POWER_SUPPLY_PTP_EXT_NOT_SUPPORTED, + POWER_SUPPLY_PTP_EXT_SUPPORTED, +}; + +enum { + POWER_SUPPLY_PTP_CURRENT_OFF = 0, + POWER_SUPPLY_PTP_CURRENT_FROM_PHONE, + POWER_SUPPLY_PTP_CURRENT_TO_PHONE, +}; + +enum { + POWER_SUPPLY_PTP_EXT_PRESENCE_UNKNOWN = 0, + POWER_SUPPLY_PTP_EXT_NOT_PRESENT, + POWER_SUPPLY_PTP_EXT_WIRED_PRESENT, + POWER_SUPPLY_PTP_EXT_WIRELESS_PRESENT, + POWER_SUPPLY_PTP_EXT_WIRED_WIRELESS_PRESENT, +}; + +enum { + POWER_SUPPLY_PTP_POWER_REQUIREMENTS_UNKNOWN = 0, + POWER_SUPPLY_PTP_POWER_NOT_REQUIRED, + POWER_SUPPLY_PTP_POWER_REQUIRED, +}; + +enum { + POWER_SUPPLY_PTP_POWER_AVAILABILITY_UNKNOWN = 0, + POWER_SUPPLY_PTP_POWER_NOT_AVAILABLE, + POWER_SUPPLY_PTP_POWER_AVAILABLE_EXTERNAL, + POWER_SUPPLY_PTP_POWER_AVAILABLE_INTERNAL, +}; + +enum { + POWER_SUPPLY_PTP_POWER_SOURCE_UNKNOWN = 0, + POWER_SUPPLY_PTP_POWER_SOURCE_NONE, + POWER_SUPPLY_PTP_POWER_SOURCE_BATTERY, + POWER_SUPPLY_PTP_POWER_SOURCE_WIRED, + POWER_SUPPLY_PTP_POWER_SOURCE_WIRELESS, + POWER_SUPPLY_PTP_POWER_SOURCE_NONE_TURBO, + POWER_SUPPLY_PTP_POWER_SOURCE_BATTERY_TURBO, + POWER_SUPPLY_PTP_POWER_SOURCE_WIRED_TURBO, + POWER_SUPPLY_PTP_POWER_SOURCE_WIRELESS_TURBO, +}; + struct device_node; struct power_supply; @@ -474,6 +570,7 @@ struct power_supply_config { char **supplied_to; size_t num_supplicants; + bool free_drv_data; }; /* Description of power supply */ @@ -529,6 +626,7 @@ struct power_supply { /* Driver private data */ void *drv_data; + bool free_pdd_on_release; /* private */ struct device dev; diff --git a/include/linux/usb/usbpd.h b/include/linux/usb/usbpd.h index 87dff93fd0dc..ee99629b807a 100644 --- a/include/linux/usb/usbpd.h +++ b/include/linux/usb/usbpd.h @@ -30,6 +30,7 @@ enum usbpd_svdm_cmd_type { #define USBPD_SVDM_EXIT_MODE 0x5 #define USBPD_SVDM_ATTENTION 0x6 +#define PD_MAX_PDO_NUM 7 /* * Implemented by client */ @@ -59,6 +60,14 @@ struct usbpd_svid_handler { bool discovered; }; +struct usbpd_pdo_info { + int pdo_pos; + int uv_max; + int uv_min; + int ua; + int type; +}; + enum plug_orientation { ORIENTATION_NONE, ORIENTATION_CC1, @@ -93,6 +102,26 @@ int usbpd_send_svdm(struct usbpd *pd, u16 svid, u8 cmd, enum usbpd_svdm_cmd_type cmd_type, int obj_pos, const u32 *vdos, int num_vdos); +/* + * Look for best match for PDO selection in Source Mode. + */ +int usbpd_select_pdo_match(struct usbpd *pd); + +/* + * Grab Data Role + */ +int usbpd_get_current_dr(struct usbpd *pd); + +/* +* support that directly set pdo which is need in Source Mode. +*/ +int usbpd_select_pdo(struct usbpd *pd, int pdo_pos, int uv, int ua); + +/* +* Transmit all effective pdo info. +*/ +int usbpd_get_pdo_info(struct usbpd *pd, struct usbpd_pdo_info *pdo_info); + /* * Get current status of CC pin orientation. * @@ -133,6 +162,19 @@ static inline int usbpd_send_svdm(struct usbpd *pd, u16 svid, u8 cmd, return -EINVAL; } +/* + * Look for best match for PDO selection in Source Mode. + */ +static inline int usbpd_select_pdo_match(struct usbpd *pd) +{ + return -EINVAL; +} + +static inline int usbpd_get_current_dr(struct usbpd *pd) +{ + return -EINVAL; +} + static inline enum plug_orientation usbpd_get_plug_orientation(struct usbpd *pd) { return ORIENTATION_NONE;