diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c index bcacfd977d14..3ef68bf15258 100644 --- a/drivers/power/supply/power_supply_sysfs.c +++ b/drivers/power/supply/power_supply_sysfs.c @@ -47,6 +47,9 @@ static const char * const power_supply_type_text[] = { "USB_HVDCP", "USB_HVDCP_3", "USB_HVDCP_3P5", "Wireless", "USB_FLOAT", "BMS", "Parallel", "Main", "USB_C_UFP", "USB_C_DFP", "Charge_Pump", +#ifdef CONFIG_MACH_XIAOMI_SM8250 + "Batt_Verify", +#endif }; static const char * const power_supply_usb_type_text[] = { @@ -215,6 +218,64 @@ static ssize_t power_supply_show_property(struct device *dev, case POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT: ret = sprintf(buf, "%lld\n", value.int64val); break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_WIRELESS_VERSION: + ret = scnprintf(buf, PAGE_SIZE, "0x%x\n", + value.intval); + break; + case POWER_SUPPLY_PROP_WIRELESS_FW_VERSION: + ret = scnprintf(buf, PAGE_SIZE, "0x%x\n", + value.intval); + break; + case POWER_SUPPLY_PROP_WIRELESS_WAKELOCK: + ret = scnprintf(buf, PAGE_SIZE, "%d\n", + value.intval); + break; + case POWER_SUPPLY_PROP_SIGNAL_STRENGTH: + ret = scnprintf(buf, PAGE_SIZE, "%d\n", + value.intval); + break; + case POWER_SUPPLY_PROP_WIRELESS_CP_EN: + ret = scnprintf(buf, PAGE_SIZE, "%d\n", + value.intval); + break; + case POWER_SUPPLY_PROP_TX_MAC: + ret = scnprintf(buf, PAGE_SIZE, "%llx\n", + value.int64val); + break; + case POWER_SUPPLY_PROP_RX_CR: + ret = scnprintf(buf, PAGE_SIZE, "%llx\n", + value.int64val); + break; + case POWER_SUPPLY_PROP_RX_CEP: + ret = scnprintf(buf, PAGE_SIZE, "%llx\n", + value.int64val); + break; + + case POWER_SUPPLY_PROP_BT_STATE: + ret = scnprintf(buf, PAGE_SIZE, "%x\n", + value.intval); + break; +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + case POWER_SUPPLY_PROP_ROMID: + case POWER_SUPPLY_PROP_DS_STATUS: + ret = scnprintf(buf, PAGE_SIZE, "%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x\n", + value.arrayval[0], value.arrayval[1], value.arrayval[2], value.arrayval[3], + value.arrayval[4], value.arrayval[5], value.arrayval[6], value.arrayval[7]); + break; + + case POWER_SUPPLY_PROP_PAGE0_DATA: + case POWER_SUPPLY_PROP_PAGE1_DATA: + case POWER_SUPPLY_PROP_PAGEDATA: + ret = scnprintf(buf, PAGE_SIZE, "%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x\n", + value.arrayval[0], value.arrayval[1], value.arrayval[2], value.arrayval[3], + value.arrayval[4], value.arrayval[5], value.arrayval[6], value.arrayval[7], + value.arrayval[8], value.arrayval[9], value.arrayval[10], value.arrayval[11], + value.arrayval[12], value.arrayval[13], value.arrayval[14], value.arrayval[15]); + break; + case POWER_SUPPLY_PROP_VERIFY_MODEL_NAME: +#endif +#endif case POWER_SUPPLY_PROP_MODEL_NAME ... POWER_SUPPLY_PROP_SERIAL_NUMBER: ret = sprintf(buf, "%s\n", value.strval); break; @@ -232,6 +293,10 @@ static ssize_t power_supply_store_property(struct device *dev, struct power_supply *psy = dev_get_drvdata(dev); enum power_supply_property psp = attr - power_supply_attrs; union power_supply_propval value; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + long val; + int64_t num_long; +#endif switch (psp) { case POWER_SUPPLY_PROP_STATUS: @@ -252,6 +317,33 @@ static ssize_t power_supply_store_property(struct device *dev, case POWER_SUPPLY_PROP_SCOPE: ret = sysfs_match_string(power_supply_scope_text, buf); break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_BT_STATE: + case POWER_SUPPLY_PROP_RX_CR: + ret = kstrtol(buf, 16, &val); + if (ret < 0) + return ret; + ret = val; + break; + case POWER_SUPPLY_PROP_RX_CEP: + ret = kstrtol(buf, 16, &val); + if (ret < 0) + return ret; + ret = val; + break; + case POWER_SUPPLY_PROP_TX_MAC: + ret = kstrtoll(buf, 16, &num_long); + if (ret < 0) + return ret; + value.int64val = num_long; + ret = power_supply_set_property(psy, psp, &value); + if (ret < 0) + return ret; + else + return count; + + break; +#endif default: ret = -EINVAL; } @@ -328,6 +420,13 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(capacity_alert_min), POWER_SUPPLY_ATTR(capacity_alert_max), POWER_SUPPLY_ATTR(capacity_level), +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_ATTR(shutdown_delay), + POWER_SUPPLY_ATTR(shutdown_delay_en), + POWER_SUPPLY_ATTR(soc_decimal), + POWER_SUPPLY_ATTR(soc_decimal_rate), + POWER_SUPPLY_ATTR(cold_thermal_level), +#endif POWER_SUPPLY_ATTR(temp), POWER_SUPPLY_ATTR(temp_max), POWER_SUPPLY_ATTR(temp_min), @@ -352,6 +451,12 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(charge_enabled), POWER_SUPPLY_ATTR(set_ship_mode), POWER_SUPPLY_ATTR(real_type), +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_ATTR(hvdcp3_type), + POWER_SUPPLY_ATTR(fake_hvdcp3), + POWER_SUPPLY_ATTR(quick_charge_type), + POWER_SUPPLY_ATTR(quick_charge_power), +#endif POWER_SUPPLY_ATTR(charge_now_raw), POWER_SUPPLY_ATTR(charge_now_error), POWER_SUPPLY_ATTR(capacity_raw), @@ -361,7 +466,14 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(step_charging_step), POWER_SUPPLY_ATTR(pin_enabled), POWER_SUPPLY_ATTR(input_suspend), +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_ATTR(battery_input_suspend), +#endif POWER_SUPPLY_ATTR(input_voltage_regulation), +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_ATTR(input_voltage_vrect), + POWER_SUPPLY_ATTR(rx_iout), +#endif POWER_SUPPLY_ATTR(input_current_max), POWER_SUPPLY_ATTR(input_current_trim), POWER_SUPPLY_ATTR(input_current_settled), @@ -386,6 +498,11 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(boost_current), POWER_SUPPLY_ATTR(safety_timer_enabled), POWER_SUPPLY_ATTR(charge_done), +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_ATTR(hiz_mode), + POWER_SUPPLY_ATTR(usb_current_now), + POWER_SUPPLY_ATTR(usb_voltage_now), +#endif POWER_SUPPLY_ATTR(flash_active), POWER_SUPPLY_ATTR(flash_trigger), POWER_SUPPLY_ATTR(force_tlim), @@ -403,11 +520,17 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(typec_mode), POWER_SUPPLY_ATTR(typec_cc_orientation), POWER_SUPPLY_ATTR(typec_power_role), +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_ATTR(typec_boost_otg_disable), +#endif POWER_SUPPLY_ATTR(typec_src_rp), POWER_SUPPLY_ATTR(pd_allowed), POWER_SUPPLY_ATTR(pd_active), POWER_SUPPLY_ATTR(pd_in_hard_reset), POWER_SUPPLY_ATTR(pd_current_max), +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_ATTR(apdo_max), +#endif POWER_SUPPLY_ATTR(pd_usb_suspend_supported), POWER_SUPPLY_ATTR(charger_temp), POWER_SUPPLY_ATTR(charger_temp_max), @@ -420,6 +543,11 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(parallel_mode), POWER_SUPPLY_ATTR(die_health), POWER_SUPPLY_ATTR(connector_health), +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_ATTR(connector_temp), + POWER_SUPPLY_ATTR(vbus_disable), + POWER_SUPPLY_ATTR(arti_vbus_enable), +#endif POWER_SUPPLY_ATTR(ctm_current_max), POWER_SUPPLY_ATTR(hw_current_max), POWER_SUPPLY_ATTR(pr_swap), @@ -429,13 +557,34 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(pd_voltage_max), POWER_SUPPLY_ATTR(pd_voltage_min), POWER_SUPPLY_ATTR(sdp_current_max), +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_ATTR(dc_thermal_levels), +#endif POWER_SUPPLY_ATTR(connector_type), POWER_SUPPLY_ATTR(parallel_batfet_mode), POWER_SUPPLY_ATTR(parallel_fcc_max), +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_ATTR(wireless_version), + POWER_SUPPLY_ATTR(wireless_fw_version), + POWER_SUPPLY_ATTR(signal_strength), + POWER_SUPPLY_ATTR(wireless_cp_en), + POWER_SUPPLY_ATTR(wireless_power_good_en), + POWER_SUPPLY_ATTR(sw_disabel_dc_en), + POWER_SUPPLY_ATTR(wireless_wakelock), + POWER_SUPPLY_ATTR(wireless_tx_id), + POWER_SUPPLY_ATTR(tx_adapter), + POWER_SUPPLY_ATTR(tx_mac), + POWER_SUPPLY_ATTR(rx_cr), + POWER_SUPPLY_ATTR(rx_cep), + POWER_SUPPLY_ATTR(bt_state), +#endif POWER_SUPPLY_ATTR(min_icl), POWER_SUPPLY_ATTR(moisture_detected), POWER_SUPPLY_ATTR(batt_profile_version), POWER_SUPPLY_ATTR(batt_full_current), +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_ATTR(warm_fake_charging), +#endif POWER_SUPPLY_ATTR(recharge_soc), POWER_SUPPLY_ATTR(hvdcp_opti_allowed), POWER_SUPPLY_ATTR(smb_en_mode), @@ -446,7 +595,14 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(clear_soh), POWER_SUPPLY_ATTR(force_recharge), POWER_SUPPLY_ATTR(fcc_stepper_enable), +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_ATTR(smb_en_allowed), + POWER_SUPPLY_ATTR(batt_2s_mode), +#endif POWER_SUPPLY_ATTR(toggle_stat), +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_ATTR(type_recheck), +#endif POWER_SUPPLY_ATTR(main_fcc_max), POWER_SUPPLY_ATTR(fg_reset), POWER_SUPPLY_ATTR(qc_opti_disable), @@ -463,6 +619,9 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(force_main_fcc), POWER_SUPPLY_ATTR(comp_clamp_level), POWER_SUPPLY_ATTR(adapter_cc_mode), +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_ATTR(non_compatible), +#endif POWER_SUPPLY_ATTR(skin_health), POWER_SUPPLY_ATTR(aicl_done), POWER_SUPPLY_ATTR(voltage_step), @@ -481,6 +640,69 @@ static struct device_attribute power_supply_attrs[] = { POWER_SUPPLY_ATTR(cp_ilim), POWER_SUPPLY_ATTR(irq_status), POWER_SUPPLY_ATTR(parallel_output_mode), +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_ATTR(cp_win_ov), + POWER_SUPPLY_ATTR(cp_passthrough_mode), + POWER_SUPPLY_ATTR(cp_passthrough_config), + POWER_SUPPLY_ATTR(cp_ovp_config), + POWER_SUPPLY_ATTR(cp_ocp_config), + POWER_SUPPLY_ATTR(cp_cfly_ss_status), + /* Bq charge pump properties */ + POWER_SUPPLY_ATTR(ti_battery_present), + POWER_SUPPLY_ATTR(ti_vbus_present), + POWER_SUPPLY_ATTR(ti_battery_voltage), + POWER_SUPPLY_ATTR(ti_battery_current), + POWER_SUPPLY_ATTR(ti_battery_temperature), + POWER_SUPPLY_ATTR(ti_bus_voltage), + POWER_SUPPLY_ATTR(ti_bus_current), + POWER_SUPPLY_ATTR(ti_bus_temperature), + POWER_SUPPLY_ATTR(ti_die_temperature), + POWER_SUPPLY_ATTR(ti_alarm_status), + POWER_SUPPLY_ATTR(ti_fault_status), + POWER_SUPPLY_ATTR(ti_reg_status), + POWER_SUPPLY_ATTR(ti_set_bus_protection_for_qc3), + POWER_SUPPLY_ATTR(ti_bus_error_status), + POWER_SUPPLY_ATTR(fastcharge_mode), + POWER_SUPPLY_ATTR(dp_dm_bq), + POWER_SUPPLY_ATTR(pd_authentication), + POWER_SUPPLY_ATTR(passthrough_curr_max), + POWER_SUPPLY_ATTR(termination_current), + POWER_SUPPLY_ATTR(ffc_termination_current), + POWER_SUPPLY_ATTR(sys_termination_current), + POWER_SUPPLY_ATTR(ffc_sys_termination_current), + POWER_SUPPLY_ATTR(vbatt_full_vol), + POWER_SUPPLY_ATTR(fcc_vbatt_full_vol), + POWER_SUPPLY_ATTR(ki_coeff_current), + POWER_SUPPLY_ATTR(recharge_vbat), + POWER_SUPPLY_ATTR(step_vfloat_index), + POWER_SUPPLY_ATTR(night_charging), +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + /* battery verify properties */ + POWER_SUPPLY_ATTR(romid), + POWER_SUPPLY_ATTR(ds_status), + POWER_SUPPLY_ATTR(pagenumber), + POWER_SUPPLY_ATTR(pagedata), + POWER_SUPPLY_ATTR(authen_result), + POWER_SUPPLY_ATTR(session_seed), + POWER_SUPPLY_ATTR(s_secret), + POWER_SUPPLY_ATTR(challenge), + POWER_SUPPLY_ATTR(auth_anon), + POWER_SUPPLY_ATTR(auth_bdconst), + POWER_SUPPLY_ATTR(page0_data), + POWER_SUPPLY_ATTR(page1_data), + POWER_SUPPLY_ATTR(verify_model_name), + POWER_SUPPLY_ATTR(maxim_batt_cycle_count), +#endif + POWER_SUPPLY_ATTR(chip_ok), + /* DIV 2 properties */ + POWER_SUPPLY_ATTR(div_2_mode), + POWER_SUPPLY_ATTR(reverse_chg_mode), + POWER_SUPPLY_ATTR(reverse_chg_state), + POWER_SUPPLY_ATTR(reverse_gpio_state), + POWER_SUPPLY_ATTR(reset_div_2_mode), + POWER_SUPPLY_ATTR(aicl_enable), + POWER_SUPPLY_ATTR(otg_state), +#endif POWER_SUPPLY_ATTR(fg_type), POWER_SUPPLY_ATTR(charger_status), /* Local extensions of type int64_t */ diff --git a/drivers/power/supply/qcom/battery.c b/drivers/power/supply/qcom/battery.c index ec45b7d1c502..f63eba417ca2 100644 --- a/drivers/power/supply/qcom/battery.c +++ b/drivers/power/supply/qcom/battery.c @@ -77,6 +77,9 @@ struct pl_data { struct power_supply *dc_psy; struct power_supply *cp_master_psy; struct power_supply *cp_slave_psy; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + struct power_supply *wireless_psy; +#endif int charge_type; int total_settled_ua; int pl_settled_ua; @@ -1206,6 +1209,19 @@ static bool is_batt_available(struct pl_data *chip) return true; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static bool is_wireless_available(struct pl_data *chip) +{ + if (!chip->wireless_psy) + chip->wireless_psy = power_supply_get_by_name("wireless"); + + if (!chip->wireless_psy) + return false; + + return true; +} +#endif + #define PARALLEL_FLOAT_VOLTAGE_DELTA_UV 50000 static int pl_fv_vote_callback(struct votable *votable, void *data, int fv_uv, const char *client) @@ -1258,6 +1274,21 @@ static int pl_fv_vote_callback(struct votable *votable, void *data, if (rc < 0) pr_err("Couldn't set force recharge rc=%d\n", rc); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + } else if (is_wireless_available(chip)) { + rc = power_supply_get_property(chip->wireless_psy, + POWER_SUPPLY_PROP_WIRELESS_POWER_GOOD_EN, + &pval); + if (pval.intval) { + pr_err("wireless re-triggering charging\n"); + rc = power_supply_set_property(chip->batt_psy, + POWER_SUPPLY_PROP_FORCE_RECHARGE, + &pval); + if (rc < 0) + pr_err("Couldn't set force recharge rc=%d\n", + rc); + } +#endif } } } diff --git a/drivers/power/supply/qcom/fg-core.h b/drivers/power/supply/qcom/fg-core.h index aa3c9b13c999..04c62fd528cd 100644 --- a/drivers/power/supply/qcom/fg-core.h +++ b/drivers/power/supply/qcom/fg-core.h @@ -85,12 +85,28 @@ #define FULL_CAPACITY 100 #define FULL_SOC_RAW 255 +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define FULL_SOC_REPORT_THR 250 +#endif #define DEBUG_BATT_SOC 67 #define BATT_MISS_SOC 50 #define ESR_SOH_SOC 50 #define EMPTY_SOC 0 +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define VBAT_CRITICAL_LOW_THR 2800 +#define EMPTY_DEBOUNCE_TIME_COUNT_MAX 5 + +#define VBAT_RESTART_FG_EMPTY_UV 3500000 +#define TEMP_THR_RESTART_FG 150 +#define RESTART_FG_START_WORK_MS 1000 +#define RESTART_FG_WORK_MS 2000 +#define EMPTY_REPORT_SOC 1 + +#define CRITICAL_HIGH_TEMP 580 +#endif + enum prof_load_status { PROFILE_MISSING, PROFILE_LOADED, @@ -326,12 +342,21 @@ struct fg_batt_props { char *batt_profile; int float_volt_uv; int vbatt_full_mv; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int ffc_vbatt_full_mv; +#endif int fastchg_curr_ma; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int nom_cap_uah; +#endif int *therm_coeffs; int therm_ctr_offset; int therm_pull_up_kohms; int *rslow_normal_coeffs; int *rslow_low_coeffs; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int ffc_term_curr_ma; +#endif }; struct fg_cyc_ctr_data { @@ -415,6 +440,28 @@ static const struct fg_pt fg_tsmc_osc_table[] = { { 90, 444992 }, }; +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define BATT_MA_AVG_SAMPLES 8 +struct batt_params { + bool update_now; + int batt_raw_soc; + int batt_soc; + int smooth_batt_soc; + int smooth_low_batt_soc; + int smooth_batt_flag; + int smooth_full_soc; + int samples_num; + int samples_index; + int batt_ma_avg_samples[BATT_MA_AVG_SAMPLES]; + int batt_ma_avg; + int batt_ma_prev; + int batt_ma; + int batt_mv; + int batt_temp; + struct timespec last_soc_change_time; +}; +#endif + struct fg_memif { struct fg_dma_address *addr_map; int num_partitions; @@ -434,6 +481,11 @@ struct fg_dev { struct power_supply *dc_psy; struct power_supply *parallel_psy; struct power_supply *pc_port_psy; +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + struct power_supply *max_verify_psy; +#endif +#endif struct fg_irq_info *irqs; struct votable *awake_votable; struct votable *delta_bsoc_irq_en_votable; @@ -450,6 +502,9 @@ struct fg_dev { struct mutex sram_rw_lock; struct mutex charge_full_lock; struct mutex qnovo_esr_ctrl_lock; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + struct timespec scale_soc_change_time; +#endif spinlock_t suspend_lock; spinlock_t awake_lock; u32 batt_soc_base; @@ -457,6 +512,9 @@ struct fg_dev { u32 mem_if_base; u32 rradc_base; u32 wa_flags; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int cycle_count; +#endif u32 esr_wakeup_ms; u32 awake_status; int batt_id_ohms; @@ -486,7 +544,18 @@ struct fg_dev { bool twm_state; bool use_dma; bool qnovo_enable; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + bool empty_restart_fg; + bool report_full; + bool profile_already_find; + bool input_present; + bool shutdown_delay; +#endif enum fg_version version; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + struct batt_params param; + struct delayed_work soc_monitor_work; +#endif bool suspended; struct completion soc_update; struct completion soc_ready; @@ -494,6 +563,13 @@ struct fg_dev { struct work_struct status_change_work; struct work_struct esr_sw_work; struct delayed_work sram_dump_work; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + struct delayed_work empty_restart_fg_work; + int fake_temp; + int fake_authentic; + int fake_chip_ok; + int maxim_cycle_count; +#endif struct work_struct esr_filter_work; struct alarm esr_filter_alarm; ktime_t last_delta_temp_time; diff --git a/drivers/power/supply/qcom/fg-util.c b/drivers/power/supply/qcom/fg-util.c index 0d152d57258e..eb0880beeb8c 100644 --- a/drivers/power/supply/qcom/fg-util.c +++ b/drivers/power/supply/qcom/fg-util.c @@ -895,27 +895,78 @@ int fg_get_msoc_raw(struct fg_dev *fg, int *val) return 0; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static bool optimized_soc_flag; +#endif int fg_get_msoc(struct fg_dev *fg, int *msoc) { int rc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int raw_msoc; +#endif rc = fg_get_msoc_raw(fg, msoc); if (rc < 0) return rc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + rc = fg_get_msoc_raw(fg, &raw_msoc); + if (rc < 0) + return rc; + + if (fg->param.smooth_batt_flag) { + if (raw_msoc >= 255) { + *msoc = FULL_CAPACITY; + } else if (raw_msoc >= 252 && !optimized_soc_flag && fg->report_full) { + *msoc = FULL_CAPACITY; + optimized_soc_flag = true; + } else if (raw_msoc >= 252 && !optimized_soc_flag && !fg->report_full) { + *msoc = FULL_CAPACITY - 1; + } else if (raw_msoc >= 245 && !optimized_soc_flag) { + *msoc = FULL_CAPACITY - 1; + } else if (raw_msoc >= 245 && optimized_soc_flag){ + *msoc = FULL_CAPACITY; + } else if (raw_msoc > 19) { + *msoc = DIV_ROUND_CLOSEST(raw_msoc * FULL_CAPACITY, FULL_SOC_RAW) + 3; + } else if (raw_msoc > 0) { + *msoc = raw_msoc / 2 + 1; + } else if (raw_msoc == 0) { + *msoc = 0; + } else { + *msoc = 0; + } + + if (raw_msoc < 245) + optimized_soc_flag = false; + } else { +#endif /* * To have better endpoints for 0 and 100, it is good to tune the * calculation discarding values 0 and 255 while rounding off. Rest * of the values 1-254 will be scaled to 1-99. DIV_ROUND_UP will not * be suitable here as it rounds up any value higher than 252 to 100. */ +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if ((*msoc >= FULL_SOC_REPORT_THR - 2) && (*msoc < FULL_SOC_RAW) && fg->report_full) { + *msoc = DIV_ROUND_CLOSEST(*msoc * FULL_CAPACITY, FULL_SOC_RAW) + 1; + if (*msoc >= FULL_CAPACITY) + *msoc = FULL_CAPACITY; + } else +#endif if (*msoc == FULL_SOC_RAW) *msoc = 100; else if (*msoc == 0) *msoc = 0; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + else if (*msoc >= FULL_SOC_REPORT_THR - 4 && *msoc <= FULL_SOC_REPORT_THR - 3 && fg->report_full) + *msoc = DIV_ROUND_CLOSEST(*msoc * FULL_CAPACITY, FULL_SOC_RAW); +#endif else *msoc = DIV_ROUND_CLOSEST((*msoc - 1) * (FULL_CAPACITY - 2), FULL_SOC_RAW - 2) + 1; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + } +#endif return 0; } diff --git a/drivers/power/supply/qcom/pmic-voter.c b/drivers/power/supply/qcom/pmic-voter.c index 838e3f7ecb62..20f6cd85d4bf 100644 --- a/drivers/power/supply/qcom/pmic-voter.c +++ b/drivers/power/supply/qcom/pmic-voter.c @@ -480,6 +480,14 @@ int vote(struct votable *votable, const char *client_str, bool enabled, int val) */ if (!votable->voted_on || (effective_result != votable->effective_result)) { +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (strcmp(votable->name, "FG_WS") != 0) { + pr_info("%s: current vote is now %d voted by %s,%d, previous voted %d\n", + votable->name, effective_result, + get_client_str(votable, effective_id), + effective_id, votable->effective_result); + } +#endif votable->effective_client_id = effective_id; votable->effective_result = effective_result; pr_debug("%s: effective vote is now %d voted by %s,%d\n", diff --git a/drivers/power/supply/qcom/qpnp-fg-gen4.c b/drivers/power/supply/qcom/qpnp-fg-gen4.c index 1274edfb1347..479c5922c0c1 100644 --- a/drivers/power/supply/qcom/qpnp-fg-gen4.c +++ b/drivers/power/supply/qcom/qpnp-fg-gen4.c @@ -20,6 +20,9 @@ #include "fg-core.h" #include "fg-reg.h" #include "fg-alg.h" +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#include +#endif #define FG_GEN4_DEV_NAME "qcom,fg-gen4" #define TTF_AWAKE_VOTER "fg_ttf_awake" @@ -203,6 +206,9 @@ #define FIRST_LOG_CURRENT_v2_WORD 471 #define FIRST_LOG_CURRENT_v2_OFFSET 0 +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define DEFAULT_FFC_TERM_CURRENT 1000 +#endif static struct fg_irq_info fg_irqs[FG_GEN4_IRQ_MAX]; /* DT parameters for FG device */ @@ -215,12 +221,26 @@ struct fg_dt_props { bool multi_profile_load; bool esr_calib_dischg; bool soc_hi_res; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + bool sun_profile_only; + bool j3s_batt_profile; +#endif bool soc_scale_mode; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + bool fg_increase_100soc_time; + bool shutdown_delay_enable; + bool cutoff_voltage_adjust_enable; + int *dec_rate_seq; + int dec_rate_len; +#endif int cutoff_volt_mv; int empty_volt_mv; int sys_min_volt_mv; int cutoff_curr_ma; int sys_term_curr_ma; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int ffc_sys_term_curr_ma; +#endif int delta_soc_thr; int vbatt_scale_thr_mv; int scale_timer_ms; @@ -249,6 +269,10 @@ struct fg_dt_props { int ki_coeff_hi_chg; int ki_coeff_lo_med_chg_thr_ma; int ki_coeff_med_hi_chg_thr_ma; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int ffc_ki_coeff_lo_med_chg_thr_ma; + int ffc_ki_coeff_med_hi_chg_thr_ma; +#endif int ki_coeff_cutoff_gain; int ki_coeff_full_soc_dischg[2]; int ki_coeff_soc[KI_COEFF_SOC_LEVELS]; @@ -281,6 +305,16 @@ struct fg_gen4_chip { struct alarm esr_fast_cal_timer; struct alarm soc_scale_alarm_timer; struct delayed_work pl_enable_work; +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + struct delayed_work battery_authentic_work; + int battery_authentic_result; + struct delayed_work ds_romid_work; + unsigned char ds_romid[8]; + struct delayed_work ds_status_work; + unsigned char ds_status[8]; + struct delayed_work ds_page0_work; + unsigned char ds_page0[16]; +#endif struct work_struct pl_current_en_work; struct completion mem_attn; struct mutex soc_scale_lock; @@ -307,6 +341,9 @@ struct fg_gen4_chip { int scale_timer; int current_now; int calib_level; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int vbat_critical_low_count; +#endif bool first_profile_load; bool ki_coeff_dischg_en; bool slope_limit_en; @@ -321,6 +358,10 @@ struct fg_gen4_chip { bool vbatt_low; bool chg_term_good; bool soc_scale_mode; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + bool fastcharge_mode_enabled; + int hw_country; +#endif }; struct bias_config { @@ -331,6 +372,12 @@ struct bias_config { static int fg_gen4_debug_mask; +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static bool is_batt_vendor_gyb; +static bool is_batt_vendor_nvt; +static bool is_low_temp_flag; +#endif + static bool fg_profile_dump; static ssize_t profile_dump_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -805,6 +852,13 @@ static int fg_gen4_get_battery_temp(struct fg_dev *fg, int *val) int rc = 0; u16 buf; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (fg->fake_temp != -EINVAL) { + *val = fg->fake_temp; + return 0; + } +#endif + rc = fg_sram_read(fg, BATT_TEMP_WORD, BATT_TEMP_OFFSET, (u8 *)&buf, 2, FG_IMA_DEFAULT); if (rc < 0) { @@ -970,6 +1024,13 @@ static int fg_gen4_get_prop_capacity(struct fg_dev *fg, int *val) struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); int rc, msoc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (!chip->dt.shutdown_delay_enable) { + *val = 1; + return 0; + } +#endif + if (is_debug_batt_id(fg)) { *val = DEBUG_BATT_SOC; return 0; @@ -995,6 +1056,11 @@ static int fg_gen4_get_prop_capacity(struct fg_dev *fg, int *val) return 0; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (fg->empty_restart_fg && (msoc == 0)) + msoc = EMPTY_REPORT_SOC; +#endif + if (chip->soc_scale_mode) { mutex_lock(&chip->soc_scale_lock); *val = chip->soc_scale_msoc; @@ -1046,6 +1112,48 @@ static int fg_gen4_get_prop_capacity_raw(struct fg_gen4_chip *chip, int *val) return 0; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static int fg_gen4_get_prop_soc_decimal_rate(struct fg_gen4_chip *chip, int *val) +{ + struct fg_dev *fg = &chip->fg; + int i, soc, rc = 0; + + rc = fg_gen4_get_prop_capacity(fg, &soc); + if (rc < 0) { + pr_err("Failed to get battery capacity, rc=%d\n", rc); + return 0; + } + + for (i = 0; i < chip->dt.dec_rate_len; i += 2) { + if (soc < chip->dt.dec_rate_seq[i]) { + *val = chip->dt.dec_rate_seq[i - 1]; + return 0; + } + } + + *val = chip->dt.dec_rate_seq[chip->dt.dec_rate_len - 1]; + + return 0; +} + +static int fg_gen4_get_prop_soc_decimal(struct fg_gen4_chip *chip, int *val) +{ + int rc; + int soc_decimal, soc_decimal_rate; + + union power_supply_propval pval = {0,}; + + rc = fg_gen4_get_prop_capacity_raw(chip, &pval.intval); + soc_decimal = pval.intval % 100; + rc = fg_gen4_get_prop_soc_decimal_rate(chip, &soc_decimal_rate); + if (soc_decimal + soc_decimal_rate >= 100) + soc_decimal -= soc_decimal_rate; + + *val = soc_decimal; + return rc; +} +#endif + static inline void get_esr_meas_current(int curr_ma, u8 *val) { switch (curr_ma) { @@ -1106,6 +1214,10 @@ static int fg_gen4_get_power(struct fg_gen4_chip *chip, int *val, bool average) return 0; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define LOW_TEMP_CUTOFF_VOL_MV 3200 +#endif + static int fg_gen4_get_prop_soc_scale(struct fg_gen4_chip *chip) { struct fg_dev *fg = &chip->fg; @@ -1865,6 +1977,15 @@ static int fg_gen4_get_batt_profile_dt_props(struct fg_gen4_chip *chip, fg->bp.fastchg_curr_ma = -EINVAL; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + rc = of_property_read_u32(profile_node, "qcom,ffc-term-current-ma", + &fg->bp.ffc_term_curr_ma); + if (rc < 0) { + pr_err("battery system current unavailable, rc:%d\n", rc); + fg->bp.ffc_term_curr_ma = -DEFAULT_FFC_TERM_CURRENT; + } +#endif + rc = of_property_read_u32(profile_node, "qcom,fg-cc-cv-threshold-mv", &fg->bp.vbatt_full_mv); if (rc < 0) { @@ -1872,6 +1993,22 @@ static int fg_gen4_get_batt_profile_dt_props(struct fg_gen4_chip *chip, fg->bp.vbatt_full_mv = -EINVAL; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + rc = of_property_read_u32(profile_node, "qcom,fg-ffc-cc-cv-threshold-mv", + &fg->bp.ffc_vbatt_full_mv); + if (rc < 0) { + pr_err("battery ffc cc_cv threshold unavailable, rc:%d\n", rc); + fg->bp.ffc_vbatt_full_mv = -EINVAL; + } + + rc = of_property_read_u32(profile_node, "qcom,nom-batt-capacity-mah", + &fg->bp.nom_cap_uah); + if (rc < 0) { + pr_err("battery nominal capacity unavailable, rc:%d\n", rc); + fg->bp.nom_cap_uah = -EINVAL; + } +#endif + if (of_find_property(profile_node, "qcom,therm-coefficients", &len)) { len /= sizeof(u32); if (len == BATT_THERM_NUM_COEFFS) { @@ -1961,6 +2098,11 @@ static int fg_gen4_get_batt_profile_dt_props(struct fg_gen4_chip *chip, return 0; } +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 +int retry_batt_profile; +#define BATT_PROFILE_RETRY_COUNT_MAX 5 +#endif + static int fg_gen4_get_batt_profile(struct fg_dev *fg) { struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); @@ -1979,9 +2121,76 @@ static int fg_gen4_get_batt_profile(struct fg_dev *fg) profile_node = of_batterydata_get_best_aged_profile(batt_node, fg->batt_id_ohms / 1000, chip->batt_age_level, &avail_age_level); +#ifndef CONFIG_BATT_VERIFY_BY_DS28E16 else profile_node = of_batterydata_get_best_profile(batt_node, fg->batt_id_ohms / 1000, NULL); +#else + else { + profile_node = ERR_PTR(-ENXIO); + /* if cmdline battery profile vendor is passed to fg driver, use cmdline result */ + if (is_batt_vendor_gyb && !fg->profile_already_find) { + pr_err("is_batt_vendor_gyb is %d\n", is_batt_vendor_gyb); + fg->profile_already_find = true; + profile_node = of_batterydata_get_best_profile(batt_node, + fg->batt_id_ohms / 1000, "j2gybm4n_4780mah"); + } else if (is_batt_vendor_nvt && !fg->profile_already_find) { + pr_err("is_batt_vendor_nvt is %d\n", is_batt_vendor_nvt); + fg->profile_already_find = true; + profile_node = of_batterydata_get_best_profile(batt_node, + fg->batt_id_ohms / 1000, "j2nvtbm4n_4780mah"); + } else { + pr_err("cmdline of batt profile is not defined, read page0 to reload file\n"); + } + // the battery is xiaomi's batt; FC code, custom id + if ((chip->ds_romid[0] == 0x9F) && ((chip->ds_romid[5] & 0xF0) == 0xF0) + && (chip->ds_romid[6] == 04) && !fg->profile_already_find) { + if ((chip->ds_page0[0] == 'C') || (chip->ds_page0[0] == 'V')) { + profile_node = of_batterydata_get_best_profile(batt_node, + fg->batt_id_ohms / 1000, "j2gybm4n_4780mah"); + } else if ((chip->ds_page0[0] == 'N') || (chip->ds_page0[0] == 'A')) { + profile_node = of_batterydata_get_best_profile(batt_node, + fg->batt_id_ohms / 1000, "j2nvtbm4n_4780mah"); + } else if ((chip->ds_page0[0] == 'S') || (chip->ds_page0[0] == 'X')) { + if (chip->dt.j3s_batt_profile) + profile_node = of_batterydata_get_best_profile(batt_node, + fg->batt_id_ohms / 1000, "j3ssun_5000mah"); + else + profile_node = of_batterydata_get_best_profile(batt_node, + fg->batt_id_ohms / 1000, "j11sun_4700mah"); + } else { + retry_batt_profile++; + } + } else if (!fg->profile_already_find) { + retry_batt_profile++; + } + + if (retry_batt_profile < BATT_PROFILE_RETRY_COUNT_MAX + && !fg->profile_already_find) { + if (profile_node == ERR_PTR(-ENXIO)) { + pr_warn("verifty battery fail. recheck after, retry:%d\n", + retry_batt_profile); + schedule_delayed_work(&fg->profile_load_work, 500); + } + } else if (!fg->profile_already_find) { + if (!chip->dt.sun_profile_only) { + pr_warn("verifty battery fail. use default profile j2gybm4n_4780mah\n"); + profile_node = of_batterydata_get_best_profile(batt_node, + fg->batt_id_ohms / 1000, "j2gybm4n_4780mah"); + } else { + if (chip->dt.j3s_batt_profile) { + pr_warn("verifty battery fail. use default profile j3ssun_5000mah\n"); + profile_node = of_batterydata_get_best_profile(batt_node, + fg->batt_id_ohms / 1000, "j3ssun_5000mah"); + } else { + pr_warn("verifty battery fail. use default profile j11sun_4700mah\n"); + profile_node = of_batterydata_get_best_profile(batt_node, + fg->batt_id_ohms / 1000, "j11sun_4700mah"); + } + } + } + } +#endif if (IS_ERR(profile_node)) return PTR_ERR(profile_node); @@ -2582,6 +2791,9 @@ static void profile_load_work(struct work_struct *work) batt_psy_initialized(fg); fg_notify_charger(fg); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + power_supply_changed(fg->fg_psy); +#endif schedule_delayed_work(&chip->ttf->ttf_work, msecs_to_jiffies(10000)); fg_dbg(fg, FG_STATUS, "profile loaded successfully"); @@ -2846,6 +3058,10 @@ static int fg_gen4_adjust_recharge_soc(struct fg_gen4_chip *chip) if (is_input_present(fg)) { if (fg->charge_done) { if (!fg->recharge_soc_adjusted) { +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (fg->health == POWER_SUPPLY_HEALTH_GOOD) + return 0; +#endif /* Get raw monotonic SOC for calculation */ rc = fg_get_msoc(fg, &msoc); if (rc < 0) { @@ -2964,7 +3180,12 @@ static int fg_gen4_charge_full_update(struct fg_dev *fg) msoc, bsoc, fg->health, fg->charge_status, fg->charge_full); if (fg->charge_done && !fg->charge_full) { +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (msoc >= 99 && (fg->health != POWER_SUPPLY_HEALTH_WARM || + fg->health != POWER_SUPPLY_HEALTH_OVERHEAT)) { +#else if (msoc >= 99 && fg->health == POWER_SUPPLY_HEALTH_GOOD) { +#endif fg_dbg(fg, FG_STATUS, "Setting charge_full to true\n"); fg->charge_full = true; } else { @@ -3294,6 +3515,10 @@ static int fg_gen4_enter_soc_scale(struct fg_gen4_chip *chip) } /* Set entry FVS SOC equal to current H/W reported SOC */ +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chip->dt.fg_increase_100soc_time) + soc = fg->param.smooth_batt_soc; +#endif chip->soc_scale_msoc = chip->prev_soc_scale_msoc = soc; chip->scale_timer = chip->dt.scale_timer_ms; /* @@ -3360,6 +3585,9 @@ static int fg_gen4_validate_soc_scale_mode(struct fg_gen4_chip *chip) { struct fg_dev *fg = &chip->fg; int rc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int vbatt_scale_mv; +#endif if (!chip->dt.soc_scale_mode) return 0; @@ -3376,9 +3604,21 @@ static int fg_gen4_validate_soc_scale_mode(struct fg_gen4_chip *chip) goto fail_soc_scale; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (is_low_temp_flag) + vbatt_scale_mv = 3400; + else + vbatt_scale_mv = chip->dt.vbatt_scale_thr_mv; + pr_info("get vbatt_scale_mv = %d, current now = %d\n", vbatt_scale_mv, chip->current_now); +#endif if (!chip->soc_scale_mode && fg->charge_status == POWER_SUPPLY_STATUS_DISCHARGING && +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chip->current_now > 0 && + chip->vbatt_avg < vbatt_scale_mv) { +#else chip->vbatt_avg < chip->dt.vbatt_scale_thr_mv) { +#endif rc = fg_gen4_enter_soc_scale(chip); if (rc < 0) { pr_err("Failed to enter SOC scale mode\n"); @@ -3389,6 +3629,17 @@ static int fg_gen4_validate_soc_scale_mode(struct fg_gen4_chip *chip) * Stay in SOC scale mode till H/W SOC catch scaled SOC * while charging. */ +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chip->dt.fg_increase_100soc_time) { + fg->param.batt_soc = chip->soc_scale_msoc - 3; + if (fg->param.batt_soc < 0) { + fg->param.batt_soc = 0; + fg->param.smooth_low_batt_soc = 0; + } + fg->param.smooth_batt_soc = chip->soc_scale_msoc; + fg_gen4_exit_soc_scale(chip); + } else +#endif if (chip->msoc_actual >= chip->soc_scale_msoc) fg_gen4_exit_soc_scale(chip); } @@ -3543,6 +3794,16 @@ static irqreturn_t fg_vbatt_low_irq_handler(int irq, void *data) if (vbatt_mv < chip->dt.cutoff_volt_mv) { if (chip->dt.rapid_soc_dec_en) { +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chip->vbat_critical_low_count++; + if (chip->vbat_critical_low_count < EMPTY_DEBOUNCE_TIME_COUNT_MAX + && vbatt_mv > VBAT_CRITICAL_LOW_THR) { + pr_info("vbat_critical_low_count:%d\n", chip->vbat_critical_low_count); + if (batt_psy_initialized(fg)) + power_supply_changed(fg->batt_psy); + return IRQ_HANDLED; + } +#endif /* * Set this flag so that slope limiter coefficient * cannot be configured during rapid SOC decrease. @@ -4049,12 +4310,69 @@ static enum alarmtimer_restart fg_soc_scale_timer(struct alarm *alarm, return ALARMTIMER_NORESTART; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static int calculate_delta_time(struct timespec *time_stamp, int *delta_time_s) +{ + struct timespec now_time; + + /* default to delta time = 0 if anything fails */ + *delta_time_s = 0; + + get_monotonic_boottime(&now_time); + *delta_time_s = (now_time.tv_sec - time_stamp->tv_sec); + + /* remember this time */ + *time_stamp = now_time; + return 0; +} + +static int calculate_average_current(struct fg_gen4_chip *chip) +{ + struct fg_dev *fg = &chip->fg; + int i; + int iavg_ma = fg->param.batt_ma; + + /* only continue if ibat has changed */ + if (fg->param.batt_ma == fg->param.batt_ma_prev) + goto unchanged; + else + fg->param.batt_ma_prev = fg->param.batt_ma; + + fg->param.batt_ma_avg_samples[fg->param.samples_index] = iavg_ma; + fg->param.samples_index = (fg->param.samples_index + 1) % BATT_MA_AVG_SAMPLES; + fg->param.samples_num++; + + if (fg->param.samples_num >= BATT_MA_AVG_SAMPLES) + fg->param.samples_num = BATT_MA_AVG_SAMPLES; + + if (fg->param.samples_num) { + iavg_ma = 0; + /* maintain a AVG_SAMPLES sample average of ibat */ + for (i = 0; i < fg->param.samples_num; i++) { + pr_debug("iavg_samples_ma[%d] = %d\n", i, fg->param.batt_ma_avg_samples[i]); + iavg_ma += fg->param.batt_ma_avg_samples[i]; + } + fg->param.batt_ma_avg = DIV_ROUND_CLOSEST(iavg_ma, fg->param.samples_num); + } + +unchanged: + pr_info("current_now_ma=%d averaged_iavg_ma=%d\n", + fg->param.batt_ma, fg->param.batt_ma_avg); + return fg->param.batt_ma_avg; +} +#endif + static void soc_scale_work(struct work_struct *work) { struct fg_gen4_chip *chip = container_of(work, struct fg_gen4_chip, soc_scale_work); struct fg_dev *fg = &chip->fg; int soc, soc_thr_percent, rc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + struct timespec last_change_time = fg->scale_soc_change_time; + int soc_changed, time_since_last_change_sec; + int delta_time = 0; +#endif if (!chip->soc_scale_mode) return; @@ -4109,10 +4427,32 @@ static void soc_scale_work(struct work_struct *work) * timer resolution to catch up the rate of decrement * of SOC. */ +#ifdef CONFIG_MACH_XIAOMI_SM8250 + calculate_delta_time(&last_change_time, &time_since_last_change_sec); + calculate_average_current(chip); + /* Calculated average current > 700mA */ + if (fg->param.batt_ma_avg > 700000) + delta_time = time_since_last_change_sec / 20; + else + delta_time = time_since_last_change_sec / 40; + + if (delta_time < 0) + delta_time = 0; + soc_changed = min(1, delta_time); + fg_dbg(fg, FG_FVSS, "get delta_time = %d, soc_changed =%d, time_since_last_change_sec = %d\n", delta_time, soc_changed, time_since_last_change_sec); +#endif chip->soc_scale_msoc = chip->prev_soc_scale_msoc - +#ifdef CONFIG_MACH_XIAOMI_SM8250 + soc_changed; +#else soc_thr_percent; +#endif chip->scale_timer = chip->dt.scale_timer_ms / (chip->prev_soc_scale_msoc - soc); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chip->prev_soc_scale_msoc != chip->soc_scale_msoc) + fg->scale_soc_change_time = last_change_time; +#endif } if (chip->soc_scale_msoc < 0) @@ -4125,6 +4465,10 @@ static void soc_scale_work(struct work_struct *work) } chip->prev_soc_scale_msoc = chip->soc_scale_msoc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + fg->param.batt_soc = chip->soc_scale_msoc; +#endif + fg_dbg(fg, FG_FVSS, "Calculated SOC=%d SOC reported=%d timer resolution=%d\n", soc, chip->soc_scale_msoc, chip->scale_timer); alarm_start_relative(&chip->soc_scale_alarm_timer, @@ -4161,6 +4505,203 @@ static void pl_enable_work(struct work_struct *work) vote(fg->awake_votable, ESR_FCC_VOTER, false, 0); } +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 +static int battery_authentic_period_ms = 1000; +#define BATTERY_AUTHENTIC_COUNT_MAX 5 +int retry_battery_authentic_result; +static void battery_authentic_work(struct work_struct *work) +{ + int rc; + //int count = 0; + union power_supply_propval pval = {0,}; + + struct fg_gen4_chip *chip = container_of(work, + struct fg_gen4_chip, + battery_authentic_work.work); + struct fg_dev *fg = &chip->fg; + + rc = power_supply_get_property(fg->fg_psy, + POWER_SUPPLY_PROP_AUTHENTIC, &pval); + if (pval.intval != true) { + retry_battery_authentic_result++; + if (retry_battery_authentic_result < BATTERY_AUTHENTIC_COUNT_MAX) { + pr_err("battery authentic work begin to restart.\n"); + schedule_delayed_work(&chip->battery_authentic_work, + msecs_to_jiffies(battery_authentic_period_ms)); + } + + if (retry_battery_authentic_result == BATTERY_AUTHENTIC_COUNT_MAX) { + pr_err("FG: authentic prop is %d\n", pval.intval); + } + } else { + pr_err("FG: authentic prop is %d\n", pval.intval); + schedule_delayed_work(&chip->ds_romid_work, + msecs_to_jiffies(0)); + schedule_delayed_work(&chip->ds_status_work, + msecs_to_jiffies(500)); + schedule_delayed_work(&chip->ds_page0_work, + msecs_to_jiffies(1000)); + } +} + +static int ds_romid_period_ms = 1000; +int retry_ds_romid; +#define DS_ROMID_COUNT_MAX 5 +static void ds_romid_work(struct work_struct *work) +{ + int rc; + //int count = 0; + union power_supply_propval pval = {0,}; + + struct fg_gen4_chip *chip = container_of(work, + struct fg_gen4_chip, + ds_romid_work.work); + struct fg_dev *fg = &chip->fg; + + rc = power_supply_get_property(fg->fg_psy, + POWER_SUPPLY_PROP_ROMID, &pval); + if (rc < 0) { + retry_ds_romid++; + if (retry_ds_romid < DS_ROMID_COUNT_MAX) { + pr_err("battery authentic work begin to restart.\n"); + schedule_delayed_work(&chip->ds_romid_work, + msecs_to_jiffies(ds_romid_period_ms)); + } + + if (retry_ds_romid == DS_ROMID_COUNT_MAX) { + pr_err("FG: romid prop is %02x %02x %02x %02x %02x %02x %02x %02x\n", + pval.arrayval[0], pval.arrayval[1], pval.arrayval[2], pval.arrayval[3], + pval.arrayval[4], pval.arrayval[5], pval.arrayval[6], pval.arrayval[7]); + } + } else { + pr_err("FG: romid prop is %02x %02x %02x %02x %02x %02x %02x %02x\n", + pval.arrayval[0], pval.arrayval[1], pval.arrayval[2], pval.arrayval[3], + pval.arrayval[4], pval.arrayval[5], pval.arrayval[6], pval.arrayval[7]); + } +} + +static int ds_status_period_ms = 1000; +#define DS_STATUS_COUNT_MAX 5 +int retry_ds_status; +static void ds_status_work(struct work_struct *work) +{ + int rc; + //int count = 0; + union power_supply_propval pval = {0,}; + + struct fg_gen4_chip *chip = container_of(work, + struct fg_gen4_chip, + ds_status_work.work); + struct fg_dev *fg = &chip->fg; + + rc = power_supply_get_property(fg->fg_psy, + POWER_SUPPLY_PROP_DS_STATUS, &pval); + if (rc < 0) { + retry_ds_status++; + if (retry_ds_status < DS_STATUS_COUNT_MAX) { + pr_err("battery authentic work begin to restart.\n"); + schedule_delayed_work(&chip->ds_status_work, + msecs_to_jiffies(ds_status_period_ms)); + } + + if (retry_ds_status == DS_STATUS_COUNT_MAX) { + pr_err("FG: ds_status prop is %02x %02x %02x %02x %02x %02x %02x %02x\n", + pval.arrayval[0], pval.arrayval[1], pval.arrayval[2], pval.arrayval[3], + pval.arrayval[4], pval.arrayval[5], pval.arrayval[6], pval.arrayval[7]); + } + } else { + pr_err("FG: ds_status prop is %02x %02x %02x %02x %02x %02x %02x %02x\n", + pval.arrayval[0], pval.arrayval[1], pval.arrayval[2], pval.arrayval[3], + pval.arrayval[4], pval.arrayval[5], pval.arrayval[6], pval.arrayval[7]); + } +} + +static int ds_page0_period_ms = 1000; +#define DS_PAGE0_COUNT_MAX 5 +int retry_ds_page0; +static void ds_page0_work(struct work_struct *work) +{ + int rc; + //int count = 0; + union power_supply_propval pval = {0,}; + + struct fg_gen4_chip *chip = container_of(work, + struct fg_gen4_chip, + ds_page0_work.work); + struct fg_dev *fg = &chip->fg; + + rc = power_supply_get_property(fg->fg_psy, + POWER_SUPPLY_PROP_PAGE0_DATA, &pval); + if (rc < 0) { + retry_ds_page0++; + if (retry_ds_page0 < DS_PAGE0_COUNT_MAX) { + pr_err("battery authentic work begin to restart.\n"); + schedule_delayed_work(&chip->ds_page0_work, + msecs_to_jiffies(ds_page0_period_ms)); + } + + if (retry_ds_page0 == DS_PAGE0_COUNT_MAX) { + pr_err("FG: ds_page0 prop is %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + pval.arrayval[0], pval.arrayval[1], pval.arrayval[2], pval.arrayval[3], + pval.arrayval[4], pval.arrayval[5], pval.arrayval[6], pval.arrayval[7], + pval.arrayval[8], pval.arrayval[9], pval.arrayval[10], pval.arrayval[11], + pval.arrayval[12], pval.arrayval[13], pval.arrayval[14], pval.arrayval[15]); + } + } else { + pr_err("FG: ds_page0 prop is %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + pval.arrayval[0], pval.arrayval[1], pval.arrayval[2], pval.arrayval[3], + pval.arrayval[4], pval.arrayval[5], pval.arrayval[6], pval.arrayval[7], + pval.arrayval[8], pval.arrayval[9], pval.arrayval[10], pval.arrayval[11], + pval.arrayval[12], pval.arrayval[13], pval.arrayval[14], pval.arrayval[15]); + } +} + +#define MAX_CYCLE_COUNT_CHECK 5 +static int sync_cycle_count(struct fg_gen4_chip *chip) +{ + struct fg_dev *fg = &chip->fg; + union power_supply_propval prop = {0, }; + static int cycle_count_check; + int cycle_count; + + get_cycle_count(chip->counter, &cycle_count); + if (!fg->max_verify_psy) { + fg->max_verify_psy = power_supply_get_by_name("batt_verify"); + if (!fg->max_verify_psy) { + pr_err("Could not find batt_verify_psy\n"); + return -ENODEV; + } + } + + if (fg->cycle_count == INT_MIN) { + if (cycle_count || cycle_count_check > MAX_CYCLE_COUNT_CHECK) + fg->cycle_count = cycle_count; + else + cycle_count_check++; + } + + if (fg->maxim_cycle_count == INT_MIN) { + power_supply_get_property(fg->max_verify_psy, + POWER_SUPPLY_PROP_MAXIM_BATT_CYCLE_COUNT, &prop); + fg->maxim_cycle_count = prop.intval; + } + + if (fg->cycle_count != INT_MIN && fg->cycle_count < cycle_count) { + prop.intval = 1; + power_supply_set_property(fg->max_verify_psy, + POWER_SUPPLY_PROP_MAXIM_BATT_CYCLE_COUNT, &prop); + power_supply_get_property(fg->max_verify_psy, + POWER_SUPPLY_PROP_MAXIM_BATT_CYCLE_COUNT, &prop); + fg->maxim_cycle_count = prop.intval; + pr_info("fg cycle_count[%d], last cycle_count[%d], dc_value[%d]\n", + cycle_count, fg->cycle_count, fg->maxim_cycle_count); + fg->cycle_count++; + } + + return 0; +} +#endif + static void status_change_work(struct work_struct *work) { struct fg_dev *fg = container_of(work, @@ -4169,6 +4710,9 @@ static void status_change_work(struct work_struct *work) int rc, batt_soc, batt_temp; bool input_present, qnovo_en; u32 batt_soc_cp; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int msoc_raw; +#endif if (fg->battery_missing) { pm_relax(fg->dev); @@ -4196,6 +4740,18 @@ static void status_change_work(struct work_struct *work) get_batt_psy_props(fg); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (fg->charge_done && !fg->report_full) { + fg->report_full = true; + } else if (!fg->charge_done && fg->report_full) { + rc = fg_get_msoc_raw(fg, &msoc_raw); + if (rc < 0) + pr_err("Error in getting msoc, rc=%d\n", rc); + if (msoc_raw < FULL_SOC_REPORT_THR - 4) + fg->report_full = false; + } +#endif + rc = fg_get_sram_prop(fg, FG_SRAM_BATT_SOC, &batt_soc); if (rc < 0) { pr_err("Failed to read battery soc rc: %d\n", rc); @@ -4209,9 +4765,15 @@ static void status_change_work(struct work_struct *work) } input_present = is_input_present(fg); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + fg->input_present = input_present; +#endif qnovo_en = is_qnovo_en(fg); cycle_count_update(chip->counter, (u32)batt_soc >> 24, fg->charge_status, fg->charge_done, input_present); +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + sync_cycle_count(chip); +#endif batt_soc_cp = div64_u64((u64)(u32)batt_soc * CENTI_FULL_SOC, BATT_SOC_32BIT); @@ -4454,7 +5016,114 @@ static struct attribute *fg_attrs[] = { }; ATTRIBUTE_GROUPS(fg); +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static int fg_gen4_set_vbatt_full_vol(struct fg_dev *fg, bool enable_ffc) +{ + int rc; + int volt; + + if (enable_ffc) + volt = fg->bp.ffc_vbatt_full_mv; + else + volt = fg->bp.vbatt_full_mv; + + if (volt < 0) + return rc; + + rc = fg_set_constant_chg_voltage(fg, volt * 1000); + if (rc < 0) { + pr_err("Error in constant chg vol rc=%d\n", rc); + return rc; + } + fg->bp.float_volt_uv = volt * 1000 + 10000; + fg_dbg(fg, FG_STATUS, "set cc-cv volt:%d float_volt_uv:%d\n", volt, fg->bp.float_volt_uv); + + return rc; +} + +static int fg_gen4_set_sys_termi_curr(struct fg_dev *fg, bool enable_ffc) +{ + struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); + u8 buf[4]; + int rc; + int curr; + + if (enable_ffc) + curr = chip->dt.ffc_sys_term_curr_ma; + else + curr = chip->dt.sys_term_curr_ma; + + if (chip->dt.ffc_sys_term_curr_ma == -EINVAL) + return 0; + + fg_encode(fg->sp, FG_SRAM_SYS_TERM_CURR, curr, buf); + rc = fg_sram_write(fg, fg->sp[FG_SRAM_SYS_TERM_CURR].addr_word, + fg->sp[FG_SRAM_SYS_TERM_CURR].addr_byte, buf, + fg->sp[FG_SRAM_SYS_TERM_CURR].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing sys_term_curr, rc=%d\n", rc); + return rc; + } + fg_dbg(fg, FG_STATUS, "set sys termi curr:%d\n", curr); + + return rc; +} + +static int fg_gen4_set_ki_coeff_curr(struct fg_dev *fg, bool enable_ffc) +{ + struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); + u8 val; + int rc; + int lo_med_curr, med_hi_curr; + + if (enable_ffc) { + lo_med_curr = chip->dt.ffc_ki_coeff_lo_med_chg_thr_ma; + med_hi_curr = chip->dt.ffc_ki_coeff_med_hi_chg_thr_ma; + } else { + lo_med_curr = chip->dt.ki_coeff_lo_med_chg_thr_ma; + med_hi_curr = chip->dt.ki_coeff_med_hi_chg_thr_ma; + } + pr_err("enable_ffc:%d, low_med_curr:%d, med_hi_curr:%d\n", enable_ffc, lo_med_curr, med_hi_curr); + + if (lo_med_curr == -EINVAL || med_hi_curr == -EINVAL) + return 0; + + fg_encode(fg->sp, FG_SRAM_KI_COEFF_LO_MED_CHG_THR, + lo_med_curr, &val); + rc = fg_sram_write(fg, + fg->sp[FG_SRAM_KI_COEFF_LO_MED_CHG_THR].addr_word, + fg->sp[FG_SRAM_KI_COEFF_LO_MED_CHG_THR].addr_byte, + &val, fg->sp[FG_SRAM_KI_COEFF_LO_MED_CHG_THR].len, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing ki_coeff_lo_med_chg_thr_ma, rc=%d\n", + rc); + return rc; + } + + fg_encode(fg->sp, FG_SRAM_KI_COEFF_MED_HI_CHG_THR, + med_hi_curr, &val); + rc = fg_sram_write(fg, + fg->sp[FG_SRAM_KI_COEFF_MED_HI_CHG_THR].addr_word, + fg->sp[FG_SRAM_KI_COEFF_MED_HI_CHG_THR].addr_byte, &val, + fg->sp[FG_SRAM_KI_COEFF_MED_HI_CHG_THR].len, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing ki_coeff_med_hi_chg_thr_ma, rc=%d\n", + rc); + return rc; + } + pr_err("==test lo_med_curr:%d, med_hi_curr:%d\n", lo_med_curr, med_hi_curr); + + return rc; +} +#endif + /* All power supply functions here */ +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define SHUTDOWN_DELAY_VOL 3300 +#define SHUTDOWN_DELAY_VOL_lOW_TEMP 3100 +#endif static int fg_psy_get_property(struct power_supply *psy, enum power_supply_property psp, @@ -4464,17 +5133,146 @@ static int fg_psy_get_property(struct power_supply *psy, struct fg_dev *fg = &chip->fg; int rc = 0, val; int64_t temp; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int vbatt_uv; + int shutdown_voltage; + static bool shutdown_delay_cancel; + static bool last_shutdown_delay; +#endif + +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + union power_supply_propval b_val = {0,}; + + if (fg->max_verify_psy == NULL) { + fg->max_verify_psy = power_supply_get_by_name("batt_verify"); + if (fg->max_verify_psy == NULL) { + pr_debug("max_verify_psy is NULL\n"); + } + } +#endif switch (psp) { +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + case POWER_SUPPLY_PROP_AUTHENTIC: + if (fg->fake_authentic != -EINVAL) { + pval->intval = fg->fake_authentic; + break; + } + + if (fg->max_verify_psy == NULL) + return -ENODATA; + + rc = power_supply_get_property(fg->max_verify_psy, + POWER_SUPPLY_PROP_AUTHEN_RESULT, &b_val); + pval->intval = b_val.intval; + chip->battery_authentic_result = pval->intval; + break; + case POWER_SUPPLY_PROP_ROMID: + if (fg->max_verify_psy == NULL) + return -ENODATA; + + rc = power_supply_get_property(fg->max_verify_psy, + POWER_SUPPLY_PROP_ROMID, &b_val); + memcpy(pval->arrayval, b_val.arrayval, 8); + memcpy(chip->ds_romid, b_val.arrayval, 8); + break; + case POWER_SUPPLY_PROP_CHIP_OK: + if (fg->fake_chip_ok != -EINVAL) { + pval->intval = fg->fake_chip_ok; + break; + } + if (fg->max_verify_psy == NULL) + return -ENODATA; + + rc = power_supply_get_property(fg->max_verify_psy, + POWER_SUPPLY_PROP_CHIP_OK, &b_val); + pval->intval = b_val.intval; + break; + case POWER_SUPPLY_PROP_DS_STATUS: + if (fg->max_verify_psy == NULL) + return -ENODATA; + + rc = power_supply_get_property(fg->max_verify_psy, + POWER_SUPPLY_PROP_DS_STATUS, &b_val); + memcpy(pval->arrayval, b_val.arrayval, 8); + memcpy(chip->ds_status, b_val.arrayval, 8); + break; + case POWER_SUPPLY_PROP_PAGE0_DATA: + if (fg->max_verify_psy == NULL) + return -ENODATA; + + rc = power_supply_get_property(fg->max_verify_psy, + POWER_SUPPLY_PROP_PAGE0_DATA, &b_val); + memcpy(pval->arrayval, b_val.arrayval, 16); + memcpy(chip->ds_page0, b_val.arrayval, 16); + break; +#endif case POWER_SUPPLY_PROP_CAPACITY: rc = fg_gen4_get_prop_capacity(fg, &pval->intval); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + //Using smooth battery capacity. + if (fg->param.batt_soc >= 0 && !chip->rapid_soc_dec_en && !chip->soc_scale_mode) + pval->intval = fg->param.batt_soc; + + if (chip->dt.fg_increase_100soc_time) { + if (fg->param.smooth_batt_soc >= 0 && !chip->rapid_soc_dec_en && !chip->soc_scale_mode) + pval->intval = fg->param.smooth_batt_soc; + } + + //shutdown delay feature + if (chip->dt.shutdown_delay_enable) { + if (pval->intval == 0) { + if (is_low_temp_flag) + shutdown_voltage = SHUTDOWN_DELAY_VOL_lOW_TEMP; + else + shutdown_voltage = SHUTDOWN_DELAY_VOL; + rc = fg_get_battery_voltage(fg, &vbatt_uv); + if (vbatt_uv/1000 > shutdown_voltage + && fg->charge_status != POWER_SUPPLY_STATUS_CHARGING) { + fg->shutdown_delay = true; + pval->intval = 1; + } else if (fg->charge_status == POWER_SUPPLY_STATUS_CHARGING + && fg->shutdown_delay) { + fg->shutdown_delay = false; + shutdown_delay_cancel = true; + pval->intval = 1; + } else { + fg->shutdown_delay = false; + if (shutdown_delay_cancel) + pval->intval = 1; + } + } else { + fg->shutdown_delay = false; + shutdown_delay_cancel = false; + } + + if (last_shutdown_delay != fg->shutdown_delay) { + last_shutdown_delay = fg->shutdown_delay; + if (fg->fg_psy) + power_supply_changed(fg->fg_psy); + } + } +#endif break; case POWER_SUPPLY_PROP_REAL_CAPACITY: rc = fg_gen4_get_prop_real_capacity(fg, &pval->intval); break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_SHUTDOWN_DELAY: + pval->intval = fg->shutdown_delay; + break; +#endif case POWER_SUPPLY_PROP_CAPACITY_RAW: rc = fg_gen4_get_prop_capacity_raw(chip, &pval->intval); break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_SOC_DECIMAL: + rc = fg_gen4_get_prop_soc_decimal(chip, &pval->intval); + break; + case POWER_SUPPLY_PROP_SOC_DECIMAL_RATE: + rc = fg_gen4_get_prop_soc_decimal_rate(chip, &pval->intval); + break; +#endif case POWER_SUPPLY_PROP_CC_SOC: rc = fg_get_sram_prop(&chip->fg, FG_SRAM_CC_SOC, &val); if (rc < 0) { @@ -4535,9 +5333,17 @@ static int fg_psy_get_property(struct power_supply *psy, pval->intval = (int)temp; break; case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (-EINVAL != fg->bp.nom_cap_uah) { + pval->intval = fg->bp.nom_cap_uah * 1000; + } else { +#endif rc = fg_gen4_get_nominal_capacity(chip, &temp); if (!rc) pval->intval = (int)temp; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + } +#endif break; case POWER_SUPPLY_PROP_CHARGE_COUNTER: rc = fg_gen4_get_charge_counter(chip, &pval->intval); @@ -4546,7 +5352,11 @@ static int fg_psy_get_property(struct power_supply *psy, rc = fg_gen4_get_charge_counter_shadow(chip, &pval->intval); break; case POWER_SUPPLY_PROP_CYCLE_COUNT: +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + pval->intval = fg->maxim_cycle_count; +#else rc = get_cycle_count(chip->counter, &pval->intval); +#endif break; case POWER_SUPPLY_PROP_CYCLE_COUNTS: rc = get_cycle_counts(chip->counter, &pval->strval); @@ -4606,6 +5416,29 @@ static int fg_psy_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CALIBRATE: pval->intval = chip->calib_level; break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_FASTCHARGE_MODE: + pval->intval = chip->fastcharge_mode_enabled; + break; + case POWER_SUPPLY_PROP_FFC_TERMINATION_CURRENT: + pval->intval = fg->bp.ffc_term_curr_ma; + break; + case POWER_SUPPLY_PROP_SYS_TERMINATION_CURRENT: + pval->intval = chip->dt.sys_term_curr_ma; + break; + case POWER_SUPPLY_PROP_FFC_SYS_TERMINATION_CURRENT: + pval->intval = chip->dt.ffc_sys_term_curr_ma; + break; + case POWER_SUPPLY_PROP_VBATT_FULL_VOL: + pval->intval = fg->bp.vbatt_full_mv; + break; + case POWER_SUPPLY_PROP_FFC_VBATT_FULL_VOL: + pval->intval = fg->bp.ffc_vbatt_full_mv; + break; + case POWER_SUPPLY_PROP_KI_COEFF_CURRENT: + pval->intval = chip->dt.ffc_ki_coeff_med_hi_chg_thr_ma; + break; +#endif default: pr_err("unsupported property %d\n", psp); rc = -EINVAL; @@ -4663,6 +5496,11 @@ static int fg_psy_set_property(struct power_supply *psy, return -EINVAL; } break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_TEMP: + fg->fake_temp = pval->intval; + break; +#endif case POWER_SUPPLY_PROP_ESR_ACTUAL: chip->esr_actual = pval->intval; break; @@ -4700,6 +5538,31 @@ static int fg_psy_set_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CALIBRATE: rc = fg_gen4_set_calibrate_level(chip, pval->intval); break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_FASTCHARGE_MODE: + chip->fastcharge_mode_enabled = pval->intval; + break; + case POWER_SUPPLY_PROP_SYS_TERMINATION_CURRENT: + rc = fg_gen4_set_sys_termi_curr(fg, pval->intval); + break; + case POWER_SUPPLY_PROP_VBATT_FULL_VOL: + rc = fg_gen4_set_vbatt_full_vol(fg, pval->intval); + break; + case POWER_SUPPLY_PROP_KI_COEFF_CURRENT: + rc = fg_gen4_set_ki_coeff_curr(fg, pval->intval); + break; + case POWER_SUPPLY_PROP_SHUTDOWN_DELAY_ENABLE: + chip->dt.shutdown_delay_enable = pval->intval; + break; +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + case POWER_SUPPLY_PROP_AUTHENTIC: + fg->fake_authentic = !!pval->intval; + break; + case POWER_SUPPLY_PROP_CHIP_OK: + fg->fake_chip_ok = !!pval->intval; + break; +#endif +#endif default: break; } @@ -4720,6 +5583,17 @@ static int fg_property_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_CLEAR_SOH: case POWER_SUPPLY_PROP_BATT_AGE_LEVEL: case POWER_SUPPLY_PROP_CALIBRATE: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_FASTCHARGE_MODE: + case POWER_SUPPLY_PROP_SYS_TERMINATION_CURRENT: + case POWER_SUPPLY_PROP_VBATT_FULL_VOL: + case POWER_SUPPLY_PROP_KI_COEFF_CURRENT: + case POWER_SUPPLY_PROP_TEMP: +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + case POWER_SUPPLY_PROP_AUTHENTIC: + case POWER_SUPPLY_PROP_CHIP_OK: +#endif +#endif return 1; default: break; @@ -4729,9 +5603,23 @@ static int fg_property_is_writeable(struct power_supply *psy, } static enum power_supply_property fg_psy_props[] = { +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + POWER_SUPPLY_PROP_AUTHENTIC, + POWER_SUPPLY_PROP_ROMID, + POWER_SUPPLY_PROP_DS_STATUS, + POWER_SUPPLY_PROP_PAGE0_DATA, + POWER_SUPPLY_PROP_CHIP_OK, +#endif POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_REAL_CAPACITY, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_SHUTDOWN_DELAY, +#endif POWER_SUPPLY_PROP_CAPACITY_RAW, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_SOC_DECIMAL, + POWER_SUPPLY_PROP_SOC_DECIMAL_RATE, +#endif POWER_SUPPLY_PROP_CC_SOC, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_VOLTAGE_NOW, @@ -4751,6 +5639,9 @@ static enum power_supply_property fg_psy_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_CYCLE_COUNT, +#endif POWER_SUPPLY_PROP_CYCLE_COUNTS, POWER_SUPPLY_PROP_SOC_REPORTING_READY, POWER_SUPPLY_PROP_CLEAR_SOH, @@ -4767,6 +5658,15 @@ static enum power_supply_property fg_psy_props[] = { POWER_SUPPLY_PROP_POWER_AVG, POWER_SUPPLY_PROP_SCALE_MODE_EN, POWER_SUPPLY_PROP_CALIBRATE, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_FASTCHARGE_MODE, + POWER_SUPPLY_PROP_FFC_TERMINATION_CURRENT, + POWER_SUPPLY_PROP_SYS_TERMINATION_CURRENT, + POWER_SUPPLY_PROP_FFC_SYS_TERMINATION_CURRENT, + POWER_SUPPLY_PROP_VBATT_FULL_VOL, + POWER_SUPPLY_PROP_FFC_VBATT_FULL_VOL, + POWER_SUPPLY_PROP_KI_COEFF_CURRENT, +#endif }; static const struct power_supply_desc fg_psy_desc = { @@ -5579,6 +6479,16 @@ static int fg_parse_ki_coefficients(struct fg_dev *fg) of_property_read_u32(node, "qcom,ki-coeff-chg-med-hi-thresh-ma", &chip->dt.ki_coeff_med_hi_chg_thr_ma); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chip->dt.ffc_ki_coeff_lo_med_chg_thr_ma = -EINVAL; + of_property_read_u32(node, "qcom,ffc-ki-coeff-chg-low-med-thresh-ma", + &chip->dt.ffc_ki_coeff_lo_med_chg_thr_ma); + + chip->dt.ffc_ki_coeff_med_hi_chg_thr_ma = -EINVAL; + of_property_read_u32(node, "qcom,ffc-ki-coeff-chg-med-hi-thresh-ma", + &chip->dt.ffc_ki_coeff_med_hi_chg_thr_ma); +#endif + chip->dt.ki_coeff_lo_med_dchg_thr_ma = 50; of_property_read_u32(node, "qcom,ki-coeff-dischg-low-med-thresh-ma", &chip->dt.ki_coeff_lo_med_dchg_thr_ma); @@ -5726,7 +6636,11 @@ static int fg_parse_esr_cal_params(struct fg_dev *fg) } #define BTEMP_DELTA_LOW 0 +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define BTEMP_DELTA_HIGH 10 +#else #define BTEMP_DELTA_HIGH 3 +#endif static void fg_gen4_parse_batt_temp_dt(struct fg_gen4_chip *chip) { @@ -5951,7 +6865,11 @@ static int fg_gen4_parse_child_nodes_dt(struct fg_gen4_chip *chip) #define DEFAULT_ESR_PULSE_THRESH_MA 47 #define DEFAULT_ESR_MEAS_CURR_MA 120 #define DEFAULT_SCALE_VBATT_THR_MV 3400 +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define DEFAULT_SCALE_ALARM_TIMER_MS 20000 +#else #define DEFAULT_SCALE_ALARM_TIMER_MS 10000 +#endif #define DEFAULT_BATT_ID_PULLUP_KOHMS 100 static int fg_gen4_parse_dt(struct fg_gen4_chip *chip) @@ -5960,6 +6878,9 @@ static int fg_gen4_parse_dt(struct fg_gen4_chip *chip) struct device_node *node = fg->dev->of_node; u32 temp; int rc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int size; +#endif if (!node) { dev_err(fg->dev, "device tree node missing\n"); @@ -6003,6 +6924,13 @@ static int fg_gen4_parse_dt(struct fg_gen4_chip *chip) chip->dt.cutoff_volt_mv = DEFAULT_CUTOFF_VOLT_MV; of_property_read_u32(node, "qcom,fg-cutoff-voltage", &chip->dt.cutoff_volt_mv); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + rc = of_property_read_u32(node, "qcom,fg-cutoff-voltage-global", &temp); + if (chip->hw_country == CountryGlobal && !rc) { + chip->dt.cutoff_volt_mv = temp; + pr_err("Global cutoff_volt=%d\n", chip->dt.cutoff_volt_mv); + } +#endif chip->dt.cutoff_curr_ma = DEFAULT_CUTOFF_CURR_MA; of_property_read_u32(node, "qcom,fg-cutoff-current", @@ -6016,6 +6944,12 @@ static int fg_gen4_parse_dt(struct fg_gen4_chip *chip) of_property_read_u32(node, "qcom,fg-sys-term-current", &chip->dt.sys_term_curr_ma); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chip->dt.ffc_sys_term_curr_ma = -EINVAL; + of_property_read_u32(node, "qcom,fg-ffc-sys-term-current", + &chip->dt.ffc_sys_term_curr_ma); +#endif + chip->dt.delta_soc_thr = DEFAULT_DELTA_SOC_THR; of_property_read_u32(node, "qcom,fg-delta-soc-thr", &chip->dt.delta_soc_thr); @@ -6026,6 +6960,29 @@ static int fg_gen4_parse_dt(struct fg_gen4_chip *chip) return -EINVAL; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + size = 0; + of_get_property(node, "qcom,soc_decimal_rate", &size); + if (size) { + chip->dt.dec_rate_seq = devm_kzalloc(fg->dev, + size, GFP_KERNEL); + if (chip->dt.dec_rate_seq) { + chip->dt.dec_rate_len = + (size / sizeof(*chip->dt.dec_rate_seq)); + if (chip->dt.dec_rate_len % 2) { + pr_err("invalid soc decimal rate seq\n"); + return -EINVAL; + } + of_property_read_u32_array(node, + "qcom,soc_decimal_rate", + chip->dt.dec_rate_seq, + chip->dt.dec_rate_len); + } else { + pr_err("error allocating memory for dec_rate_seq\n"); + } + } +#endif + chip->dt.esr_timer_chg_fast[TIMER_RETRY] = -EINVAL; chip->dt.esr_timer_chg_fast[TIMER_MAX] = -EINVAL; rc = fg_parse_dt_property_u32_array(node, "qcom,fg-esr-timer-chg-fast", @@ -6059,6 +7016,13 @@ static int fg_gen4_parse_dt(struct fg_gen4_chip *chip) chip->dt.force_load_profile = of_property_read_bool(node, "qcom,fg-force-load-profile"); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chip->dt.shutdown_delay_enable = of_property_read_bool(node, + "qcom,shutdown-delay-enable"); + chip->dt.cutoff_voltage_adjust_enable = of_property_read_bool(node, + "qcom,cutoff-voltage-adjust-enable"); +#endif + fg_gen4_parse_cl_params_dt(chip); fg_gen4_parse_batt_temp_dt(chip); @@ -6122,6 +7086,13 @@ static int fg_gen4_parse_dt(struct fg_gen4_chip *chip) chip->dt.multi_profile_load = of_property_read_bool(node, "qcom,multi-profile-load"); chip->dt.soc_hi_res = of_property_read_bool(node, "qcom,soc-hi-res"); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chip->dt.sun_profile_only = of_property_read_bool(node, "qcom,sun-profile-only"); + chip->dt.j3s_batt_profile = of_property_read_bool(node, "qcom,j3s-batt-profile"); + + chip->dt.fg_increase_100soc_time = of_property_read_bool(node, "qcom,fg-increase-100soc-time"); + fg->param.smooth_batt_flag = of_property_read_bool(node, "qcom,fg-increase-100soc-time-2"); +#endif chip->dt.sys_min_volt_mv = DEFAULT_SYS_MIN_VOLT_MV; of_property_read_u32(node, "qcom,fg-sys-min-voltage", @@ -6133,6 +7104,288 @@ static int fg_gen4_parse_dt(struct fg_gen4_chip *chip) return 0; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define SCALE_SOC 3 +#define DETAL_SOC 1 +//#define FFC_SYS_TERMI_CURRENT -1280000 +static int scale_count; +extern bool off_charge_flag; +static void fg_battery_soc_smooth_tracking(struct fg_gen4_chip *chip) +{ + struct fg_dev *fg = &chip->fg; + int delta_time = 0; + int soc_changed; + int last_batt_soc = fg->param.batt_soc; + int time_since_last_change_sec; + int last_smooth_batt_soc = fg->param.smooth_batt_soc; + + struct timespec last_change_time = fg->param.last_soc_change_time; + + calculate_delta_time(&last_change_time, &time_since_last_change_sec); + pr_info("entry:smooth_batt_soc%d\n", fg->param.smooth_batt_soc); + + /* calculate average ibat */ + calculate_average_current(chip); + if (fg->param.batt_temp > 150) { + /* Battery in normal temperture */ + if (fg->param.batt_ma_avg > 1000000) + /* Heavy loading current, ignore battery soc limit*/ + delta_time = time_since_last_change_sec / 10; + else if (fg->param.batt_ma < 0 || + (abs(fg->param.batt_raw_soc - fg->param.batt_soc) > 2)) + delta_time = time_since_last_change_sec / 20; + else + delta_time = time_since_last_change_sec / 60; + } else { + /* Calculated average current > 1000mA */ + if ((fg->param.batt_ma_avg > 1000000) || + (abs(fg->param.batt_raw_soc - fg->param.batt_soc) > 2)) + /* Heavy loading current, ignore battery soc limit*/ + delta_time = time_since_last_change_sec / 10; + else + delta_time = time_since_last_change_sec / 20; + } + + if (delta_time < 0) + delta_time = 0; + + soc_changed = min(1, delta_time); + + if (last_batt_soc >= 0) { + if (last_batt_soc != 100 + && fg->param.batt_raw_soc >= 95 + && fg->charge_status == POWER_SUPPLY_STATUS_FULL) + // Unlikely status + last_batt_soc = fg->param.update_now ? + 100 : last_batt_soc + soc_changed; + else if (last_batt_soc < fg->param.batt_raw_soc && + fg->param.batt_ma < 0) + /* Battery in charging status + * update the soc when resuming device + */ + last_batt_soc = fg->param.update_now ? + fg->param.batt_raw_soc : last_batt_soc + soc_changed; + else if (last_batt_soc > fg->param.batt_raw_soc + && fg->param.batt_ma > 0) + /* Battery in discharging status + * update the soc when resuming device + */ + last_batt_soc = fg->param.update_now ? + fg->param.batt_raw_soc : last_batt_soc - soc_changed; + + fg->param.update_now = false; + } else { + last_batt_soc = fg->param.batt_raw_soc; + } + + if (chip->dt.fg_increase_100soc_time) { + fg->param.smooth_batt_soc = last_batt_soc + SCALE_SOC; + if (fg->param.smooth_batt_soc > 100) + fg->param.smooth_batt_soc = 100; + } + + if (fg->param.batt_soc != last_batt_soc) { + fg->param.batt_soc = last_batt_soc; + fg->param.last_soc_change_time = last_change_time; + if (chip->dt.fg_increase_100soc_time) { + if ((fg->charge_status == POWER_SUPPLY_STATUS_CHARGING) && fg->param.batt_soc > 96 && scale_count < SCALE_SOC) { + fg->param.smooth_batt_soc -= DETAL_SOC; + fg->param.smooth_full_soc = fg->param.smooth_batt_soc; + scale_count++; + } else { + scale_count = 0; + fg->param.smooth_full_soc = 0; + } + } + if (batt_psy_initialized(fg)) + power_supply_changed(fg->batt_psy); + } + + if (chip->dt.fg_increase_100soc_time) { + if (fg->param.smooth_full_soc > 0) { + //force 100soc at sys termi current + //if ((fg->param.smooth_full_soc == 100) + // && (chip->fastcharge_mode_enabled == 1) + // && (fg->param.batt_ma > FFC_SYS_TERMI_CURRENT)) + // fg->param.smooth_full_soc == 99; + + fg->param.smooth_batt_soc = fg->param.smooth_full_soc; + } + + if ((fg->charge_status == POWER_SUPPLY_STATUS_CHARGING || fg->param.batt_ma < 0) + && (last_batt_soc < 3) + && off_charge_flag + && (fg->param.smooth_low_batt_soc < fg->param.smooth_batt_soc)) { + pr_info("smooth_low_batt_soc%d", fg->param.smooth_low_batt_soc); + pr_info("smooth_batt_soc%d", fg->param.smooth_batt_soc); + fg->param.smooth_low_batt_soc += DETAL_SOC; + fg->param.smooth_batt_soc = fg->param.smooth_low_batt_soc; + } + + pr_info("last_smooth_batt_soc:%d, smooth_batt_soc:%d\n", last_smooth_batt_soc, fg->param.smooth_batt_soc); + + if (last_smooth_batt_soc > 0) { + /*compare with last soc. avoid soc jump*/ + if ((fg->param.smooth_batt_soc - last_smooth_batt_soc) > 1) { + fg->param.smooth_batt_soc = last_smooth_batt_soc + DETAL_SOC; + pr_info("batt_soc:++\n"); + } else if ((last_smooth_batt_soc - fg->param.smooth_batt_soc) > 1) { + fg->param.smooth_batt_soc = last_smooth_batt_soc - DETAL_SOC; + pr_info("batt_soc:--\n"); + } + pr_info("last_smooth_batt_soc:%d, smooth_batt_soc:%d\n", last_smooth_batt_soc, fg->param.smooth_batt_soc); + + /*keep soc change with charging status*/ + if ((fg->param.batt_ma < 0) && (fg->param.smooth_batt_soc < last_smooth_batt_soc)) { + pr_info("soc should not fall\n"); + fg->param.smooth_batt_soc = last_smooth_batt_soc; + } else if ((fg->param.batt_ma > 0) && (fg->param.smooth_batt_soc > last_smooth_batt_soc)) { + pr_info("soc should not rise\n"); + fg->param.smooth_batt_soc = last_smooth_batt_soc; + } + } + + //fix batt soc jump after reboot in low batt soc + if ((last_smooth_batt_soc == 0) + && !off_charge_flag + && (last_batt_soc >= 2) + && (last_batt_soc <= 5)) { + fg->param.smooth_batt_soc = last_batt_soc; + pr_info("fix soc jump after reboot, smooth_batt_soc:%d\n", fg->param.smooth_batt_soc); + } + } + /* if battery temperature is above critical high threshold, report it */ + if (fg->param.batt_temp > CRITICAL_HIGH_TEMP) { + if (batt_psy_initialized(fg)) + power_supply_changed(fg->batt_psy); + } + + pr_info("soc:%d, last_soc:%d, raw_soc:%d, soc_changed:%d, batt_ma:%d, smooth_low_batt_soc:%d, smooth_soc: %d\n", + fg->param.batt_soc, last_batt_soc, + fg->param.batt_raw_soc, soc_changed, fg->param.batt_ma, fg->param.smooth_low_batt_soc, fg->param.smooth_batt_soc); +} + +#define RESTART_FG_MONITOR_SOC_WAIT_PER_MS 30000 +static void empty_restart_fg_work(struct work_struct *work) +{ + struct fg_dev *fg = container_of(work, struct fg_dev, + empty_restart_fg_work.work); + union power_supply_propval prop = {0, }; + int usb_present = 0; + int rc; + + if (usb_psy_initialized(fg)) { + rc = power_supply_get_property(fg->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (rc < 0) { + pr_err("Couldn't read usb present prop rc=%d\n", rc); + return; + } + usb_present = prop.intval; + } + + /* only when usb is absent, restart fg */ + if (!usb_present) { + if (fg->profile_load_status == PROFILE_LOADED) { + pr_info("soc empty after cold to warm, need to restart fg\n"); + fg->empty_restart_fg = true; + rc = fg_restart(fg, SOC_READY_WAIT_TIME_MS); + if (rc < 0) { + pr_err("Error in restarting FG, rc=%d\n", rc); + fg->empty_restart_fg = false; + return; + } + pr_info("FG restart done\n"); + if (batt_psy_initialized(fg)) + power_supply_changed(fg->batt_psy); + cancel_delayed_work_sync(&fg->soc_monitor_work); + schedule_delayed_work(&fg->soc_monitor_work, + msecs_to_jiffies(RESTART_FG_MONITOR_SOC_WAIT_PER_MS)); + } else { + schedule_delayed_work( + &fg->empty_restart_fg_work, + msecs_to_jiffies(RESTART_FG_WORK_MS)); + } + } +} +static int fg_dynamic_set_cutoff_voltage(struct fg_dev *fg, + int cut_off_mv) +{ + int rc; + u8 buf[4]; + + pr_err("set dynamic cutoff voltage to: %d\n", cut_off_mv); + + fg_encode(fg->sp, FG_SRAM_CUTOFF_VOLT, cut_off_mv, buf); + rc = fg_sram_write(fg, fg->sp[FG_SRAM_CUTOFF_VOLT].addr_word, + fg->sp[FG_SRAM_CUTOFF_VOLT].addr_byte, buf, + fg->sp[FG_SRAM_CUTOFF_VOLT].len, FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in writing cutoff_volt, rc=%d\n", rc); + return rc; + } + + return rc; +} + +#define LOW_DISCHARGE_TEMP_TRH 150 +#define MONITOR_SOC_WAIT_READY 500 +#define MONITOR_SOC_WAIT_MS 1000 +#define MONITOR_SOC_WAIT_PER_MS 10000 +static void soc_monitor_work(struct work_struct *work) +{ + int rc; + struct fg_dev *fg = container_of(work, + struct fg_dev, + soc_monitor_work.work); + struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg); + + // Update battery information + rc = fg_get_battery_current(fg, &fg->param.batt_ma); + if (rc < 0) + pr_err("failded to get battery current, rc=%d\n", rc); + + rc = fg_gen4_get_prop_capacity(fg, &fg->param.batt_raw_soc); + if (rc < 0) + pr_err("failed to get battery capacity, rc=%d\n", rc); + + rc = fg_gen4_get_battery_temp(fg, &fg->param.batt_temp); + if (rc < 0) + pr_err("failed to get battery temperature, rc=%d\n", rc); + + if (chip->dt.cutoff_voltage_adjust_enable) { + if (fg->param.batt_temp < LOW_DISCHARGE_TEMP_TRH ) { + chip->dt.cutoff_volt_mv = LOW_TEMP_CUTOFF_VOL_MV; + is_low_temp_flag = true; + } else + is_low_temp_flag = false; + + fg_dynamic_set_cutoff_voltage(fg, chip->dt.cutoff_volt_mv); + if (rc < 0) + pr_err("fg_dynamic_set_cutoff_voltage set failed\n"); + } + + if (fg->soc_reporting_ready) + fg_battery_soc_smooth_tracking(chip); + + pr_info("soc:%d, raw_soc:%d, c:%d, s:%d\n", + fg->param.batt_soc, fg->param.batt_raw_soc, + fg->param.batt_ma, fg->charge_status); + + if (chip->dt.fg_increase_100soc_time) { + if (!fg->soc_reporting_ready) + schedule_delayed_work(&fg->soc_monitor_work, + msecs_to_jiffies(MONITOR_SOC_WAIT_READY)); + else + schedule_delayed_work(&fg->soc_monitor_work, + msecs_to_jiffies(MONITOR_SOC_WAIT_PER_MS)); + } else { + schedule_delayed_work(&fg->soc_monitor_work, + msecs_to_jiffies(MONITOR_SOC_WAIT_PER_MS)); + } +} +#endif + static void fg_gen4_cleanup(struct fg_gen4_chip *chip) { struct fg_dev *fg = &chip->fg; @@ -6146,6 +7399,9 @@ static void fg_gen4_cleanup(struct fg_gen4_chip *chip) cancel_delayed_work_sync(&fg->profile_load_work); cancel_delayed_work_sync(&fg->sram_dump_work); cancel_work_sync(&chip->pl_current_en_work); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + cancel_delayed_work_sync(&fg->empty_restart_fg_work); +#endif power_supply_unreg_notifier(&fg->nb); debugfs_remove_recursive(fg->dfs_root); @@ -6169,6 +7425,37 @@ static void fg_gen4_cleanup(struct fg_gen4_chip *chip) dev_set_drvdata(fg->dev, NULL); } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define IBAT_OLD_WORD 317 +#define IBAT_OLD_OFFSET 0 +#define BATT_CURRENT_NUMR 488281 +#define BATT_CURRENT_DENR 1000 +int fg_get_batt_isense(struct fg_dev *fg, int *val) +{ + int rc; + u8 buf[2]; + int64_t temp = 0; + + rc = fg_sram_read(fg, IBAT_OLD_WORD, IBAT_OLD_OFFSET, buf, 2, + FG_IMA_DEFAULT); + if (rc < 0) { + pr_err("Error in reading %04x[%d] rc=%d\n", IBAT_OLD_WORD, + IBAT_OLD_OFFSET, rc); + return rc; + } + + temp = buf[0] | buf[1] << 8; + + /* Sign bit is bit 15 */ + temp = sign_extend32(temp, 15); + *val = div_s64((s64)temp * BATT_CURRENT_NUMR, BATT_CURRENT_DENR); + pr_info("read batt isense: %d[%d]%d\n", + (*val)/10, *val, (*val)/1000); + + return 0; +} +#endif + static void fg_gen4_post_init(struct fg_gen4_chip *chip) { int i; @@ -6215,10 +7502,35 @@ static int fg_gen4_probe(struct platform_device *pdev) fg->charge_status = -EINVAL; fg->online_status = -EINVAL; fg->batt_id_ohms = -EINVAL; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + fg->cycle_count = INT_MIN; +#endif chip->ki_coeff_full_soc[0] = -EINVAL; chip->ki_coeff_full_soc[1] = -EINVAL; chip->esr_soh_cycle_count = -EINVAL; chip->calib_level = -EINVAL; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + fg->param.batt_soc = -EINVAL; + fg->fake_temp = -EINVAL; + fg->fake_authentic = -EINVAL; + fg->fake_chip_ok = -EINVAL; + fg->maxim_cycle_count = INT_MIN; + chip->vbat_critical_low_count = 0; + fg->param.smooth_batt_soc = 0; + fg->param.smooth_low_batt_soc = 0; + fg->param.smooth_full_soc = 0; +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + chip->battery_authentic_result = -EINVAL; + memset(chip->ds_romid, 0, 8); + memset(chip->ds_status, 0, 8); + memset(chip->ds_page0, 0, 16); + retry_batt_profile = 0; + retry_battery_authentic_result = 0; + retry_ds_romid = 0; + retry_ds_status = 0; + retry_ds_page0 = 0; +#endif +#endif chip->soh = -EINVAL; fg->regmap = dev_get_regmap(fg->dev->parent, NULL); if (!fg->regmap) { @@ -6241,6 +7553,16 @@ static int fg_gen4_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&fg->sram_dump_work, sram_dump_work); INIT_DELAYED_WORK(&chip->pl_enable_work, pl_enable_work); INIT_WORK(&chip->pl_current_en_work, pl_current_en_work); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + INIT_DELAYED_WORK(&fg->soc_monitor_work, soc_monitor_work); + INIT_DELAYED_WORK(&fg->empty_restart_fg_work, empty_restart_fg_work); +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + INIT_DELAYED_WORK(&chip->battery_authentic_work, battery_authentic_work); + INIT_DELAYED_WORK(&chip->ds_romid_work, ds_romid_work); + INIT_DELAYED_WORK(&chip->ds_status_work, ds_status_work); + INIT_DELAYED_WORK(&chip->ds_page0_work, ds_page0_work); +#endif +#endif fg->awake_votable = create_votable("FG_WS", VOTE_SET_ANY, fg_awake_cb, fg); @@ -6294,6 +7616,11 @@ static int fg_gen4_probe(struct platform_device *pdev) goto exit; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chip->hw_country = get_hw_country_version(); + dev_err(fg->dev, "hw_country: %d\n", chip->hw_country); +#endif + rc = fg_gen4_parse_dt(chip); if (rc < 0) { dev_err(fg->dev, "Error in reading DT parameters, rc:%d\n", @@ -6338,6 +7665,9 @@ static int fg_gen4_probe(struct platform_device *pdev) goto exit; } +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + fg->max_verify_psy = power_supply_get_by_name("batt_verify"); +#endif /* Register the power supply */ fg_psy_cfg.drv_data = fg; fg_psy_cfg.of_node = fg->dev->of_node; @@ -6349,6 +7679,13 @@ static int fg_gen4_probe(struct platform_device *pdev) goto exit; } +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + if (chip->battery_authentic_result != true) { + schedule_delayed_work(&chip->battery_authentic_work, + msecs_to_jiffies(0)); + } +#endif + fg->nb.notifier_call = fg_notifier_cb; rc = power_supply_reg_notifier(&fg->nb); if (rc < 0) { @@ -6417,6 +7754,27 @@ static int fg_gen4_probe(struct platform_device *pdev) fg_gen4_post_init(chip); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chip->dt.fg_increase_100soc_time) { + schedule_delayed_work(&fg->soc_monitor_work, + msecs_to_jiffies(0)); + } else { + schedule_delayed_work(&fg->soc_monitor_work, + msecs_to_jiffies(5*MONITOR_SOC_WAIT_MS)); + } + + /* + * if vbat is above 3.5V and msoc is 0% and battery temperature is + * above 15 degree, we restart fg to do new first soc calculate to + * improve user experience when device is shutdown in cold then + * try to power on in normal temperature room. + */ + if ((volt_uv >= VBAT_RESTART_FG_EMPTY_UV) + && (msoc == 0) && (batt_temp >= TEMP_THR_RESTART_FG)) + schedule_delayed_work(&fg->empty_restart_fg_work, + msecs_to_jiffies(RESTART_FG_START_WORK_MS)); +#endif + pr_debug("FG GEN4 driver probed successfully\n"); return 0; exit: @@ -6437,6 +7795,9 @@ static void fg_gen4_shutdown(struct platform_device *pdev) struct fg_gen4_chip *chip = dev_get_drvdata(&pdev->dev); struct fg_dev *fg = &chip->fg; int rc, bsoc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int msoc; +#endif fg_unregister_interrupts(fg, chip, FG_GEN4_IRQ_MAX); @@ -6456,7 +7817,18 @@ static void fg_gen4_shutdown(struct platform_device *pdev) return; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + rc = fg_gen4_get_prop_capacity(fg, &msoc); + if (rc < 0) { + pr_err("Error in getting capacity, rc=%d\n", rc); + return; + } + + /* if msoc is 100% when shutdown, write full soc for next reboot */ + if (fg->charge_full || (msoc == 100)) { +#else if (fg->charge_full) { +#endif /* We need 2 most significant bytes here */ bsoc = (u32)bsoc >> 16; @@ -6490,11 +7862,23 @@ static int fg_gen4_resume(struct device *dev) { struct fg_gen4_chip *chip = dev_get_drvdata(dev); struct fg_dev *fg = &chip->fg; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int val = 0; + if (!fg->input_present) + fg_get_batt_isense(fg, &val); +#endif schedule_delayed_work(&chip->ttf->ttf_work, 0); if (fg_sram_dump) schedule_delayed_work(&fg->sram_dump_work, msecs_to_jiffies(fg_sram_dump_period_ms)); + +#ifdef CONFIG_MACH_XIAOMI_SM8250 + fg->param.update_now = true; + schedule_delayed_work(&fg->soc_monitor_work, + msecs_to_jiffies(MONITOR_SOC_WAIT_MS)); +#endif + return 0; } @@ -6519,6 +7903,21 @@ static struct platform_driver fg_gen4_driver = { .shutdown = fg_gen4_shutdown, }; +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static int __init early_parse_batt_profile_vendor_id(char *p) +{ + if (p) { + if (!strcmp(p, "0A")) + is_batt_vendor_gyb = true; + else if (!strcmp(p, "0B")) + is_batt_vendor_nvt = true; + } + + return 0; +} +early_param("androidboot.profile_vendor_id", early_parse_batt_profile_vendor_id); +#endif + module_platform_driver(fg_gen4_driver); MODULE_DESCRIPTION("QPNP Fuel gauge GEN4 driver"); diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c index 8a9c98ec73b5..6ee62464e0dd 100644 --- a/drivers/power/supply/qcom/qpnp-smb5.c +++ b/drivers/power/supply/qcom/qpnp-smb5.c @@ -22,6 +22,9 @@ #include #include "smb5-reg.h" #include "smb5-lib.h" +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#include "step-chg-jeita.h" +#endif #include "schgm-flash.h" static struct smb_params smb5_pmi632_params = { @@ -213,8 +216,14 @@ struct smb_dt_props { int auto_recharge_vbat_mv; int wd_bark_time; int wd_snarl_time_cfg; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int batt_unverify_fcc_ua; +#endif int batt_profile_fcc_ua; int batt_profile_fv_uv; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int non_fcc_batt_profile_fv_uv; +#endif int term_current_src; int term_current_thresh_hi_ma; int term_current_thresh_lo_ma; @@ -227,6 +236,10 @@ struct smb5 { struct smb_dt_props dt; }; +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static struct smb_charger *__smbchg; +#endif + static int __debug_mask; static ssize_t pd_disabled_show(struct device *dev, struct device_attribute @@ -279,9 +292,41 @@ static ssize_t weak_chg_icl_ua_store(struct device *dev, struct device_attribute } static DEVICE_ATTR_RW(weak_chg_icl_ua); +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static ssize_t thermal_fcc_override_show(struct device *dev, struct device_attribute + *attr, char *buf) +{ + struct smb5 *chip = dev_get_drvdata(dev); + struct smb_charger *chg = &chip->chg; + + return snprintf(buf, PAGE_SIZE, "%d\n", chg->thermal_fcc_override); +} + +static ssize_t thermal_fcc_override_store(struct device *dev, struct device_attribute + *attr, const char *buf, size_t count) +{ + int val; + struct smb5 *chip = dev_get_drvdata(dev); + struct smb_charger *chg = &chip->chg; + + if (kstrtos32(buf, 0, &val)) + return -EINVAL; + + chg->thermal_fcc_override = val; + if (chg->fcc_votable && chg->thermal_fcc_override > 0) + vote(chg->fcc_votable, THERMAL_FCC_OVERRIDE_VOTER, true, val); + return count; +} + +static DEVICE_ATTR_RW(thermal_fcc_override); +#endif + static struct attribute *smb5_attrs[] = { &dev_attr_pd_disabled.attr, &dev_attr_weak_chg_icl_ua.attr, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + &dev_attr_thermal_fcc_override.attr, +#endif NULL, }; ATTRIBUTE_GROUPS(smb5); @@ -429,6 +474,166 @@ static int smb5_configure_internal_pull(struct smb_charger *chg, int type, return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static int read_step_chg_range_data_from_node(struct device_node *node, + const char *prop_str, struct six_pin_step_data *ranges) +{ + int rc = 0, length, per_tuple_length, tuples; + + if (!node || !prop_str || !ranges) { + pr_err("Invalid parameters passed\n"); + return -EINVAL; + } + + rc = of_property_count_elems_of_size(node, prop_str, sizeof(u32)); + if (rc < 0) { + pr_err("Count %s failed, rc=%d\n", prop_str, rc); + return rc; + } + + length = rc; + per_tuple_length = sizeof(struct six_pin_step_data) / sizeof(u32); + if (length % per_tuple_length) { + pr_err("%s length (%d) should be multiple of %d\n", + prop_str, length, per_tuple_length); + return -EINVAL; + } + tuples = length / per_tuple_length; + + if (tuples > MAX_STEP_ENTRIES) { + pr_err("too many entries(%d), only %d allowed\n", + tuples, MAX_STEP_ENTRIES); + return -EINVAL; + } + + rc = of_property_read_u32_array(node, prop_str, + (u32 *)ranges, length); + if (rc) { + pr_err("Read %s failed, rc=%d\n", prop_str, rc); + return rc; + } + + return rc; +} + +static int smb5_charge_step_charge_init(struct smb_charger *chg, + struct device_node *node) +{ + int rc = 0; + + rc = read_step_chg_range_data_from_node(node, + "mi,six-pin-step-chg-params", + chg->six_pin_step_cfg); + if (rc < 0) { + pr_debug("Read mi,six-pin-step-chg-params failed charger node, rc=%d\n", + rc); + chg->six_pin_step_charge_enable = false; + } + + return rc; +} + +static int smb5_charge_gpio_init(struct smb_charger *chg, struct device_node *node) +{ + int rc = 0; + + if (chg->wireless_bq) { + chg->ncp3902_en_gpio = of_get_named_gpio(node, "mi,ncp-enable", 0); + if (gpio_is_valid(chg->ncp3902_en_gpio)) { + rc = gpio_request(chg->ncp3902_en_gpio, "ncp3902-en"); + if (rc) + pr_err("failed to request ncp3902_en_gpio rc = %d\n", rc); + } + + chg->ncp3902_pass_en_gpio = of_get_named_gpio(node, "mi,ncp-pass-enable", 0); + if (gpio_is_valid(chg->ncp3902_pass_en_gpio)) { + rc = gpio_request(chg->ncp3902_pass_en_gpio, "ncp-pass-en"); + if (rc) + pr_err("failed to request otg-en rc = %d\n", rc); + } + + chg->dc_chg_gpio = of_get_named_gpio(node, "mi,dc-chg-enable", 0); + if (gpio_is_valid(chg->dc_chg_gpio)) { + rc = gpio_request(chg->dc_chg_gpio, "dc_chg_enable"); + if (rc) + pr_err("failed to request dc chg enable rc = %d\n", rc); + /* set dc chg enable to default output low */ + rc = gpio_direction_output(chg->dc_chg_gpio, 0); + if (rc) + pr_err("unable to set output low rc = %d\n", rc); + } + + chg->bq_en_gpio = of_get_named_gpio(node, "mi,wl-bq-enable", 0); + if (gpio_is_valid(chg->bq_en_gpio)) { + rc = gpio_request(chg->bq_en_gpio, "wl_bq en"); + if (rc) + pr_err("failed to request bq_en_gpio rc = %d\n", rc); + /* set wl_bq enable to default output low */ + rc = gpio_direction_output(chg->bq_en_gpio, 0); + if (rc) + pr_err("unable to set output low rc = %d\n", rc); + } + + chg->rx_hw_sleep_gpio = of_get_named_gpio(node, "mi,rx-hw-sleep", 0); + if (gpio_is_valid(chg->rx_hw_sleep_gpio)) { + rc = gpio_request(chg->rx_hw_sleep_gpio, "rx_hw_sleep"); + if (rc) + pr_err("failed to request rx_hw_sleep_gpio rc = %d\n", rc); + } + } + + chg->smb5_pinctrl = devm_pinctrl_get(chg->dev); + if (IS_ERR_OR_NULL(chg->smb5_pinctrl)) { + dev_err(chg->dev, "No pinctrl config specified\n"); + rc = PTR_ERR(chg->dev); + return rc; + } + chg->smb5_gpio_active = + pinctrl_lookup_state(chg->smb5_pinctrl, "smb5_active"); + if (IS_ERR_OR_NULL(chg->smb5_gpio_active)) { + dev_err(chg->dev, "No active config specified\n"); + rc = PTR_ERR(chg->smb5_gpio_active); + return rc; + } + chg->smb5_gpio_suspend = + pinctrl_lookup_state(chg->smb5_pinctrl, "smb5_suspend"); + if (IS_ERR_OR_NULL(chg->smb5_gpio_suspend)) { + dev_err(chg->dev, "No suspend config specified\n"); + rc = PTR_ERR(chg->smb5_gpio_suspend); + return rc; + } + + rc = pinctrl_select_state(chg->smb5_pinctrl, + chg->smb5_gpio_active); + if (rc < 0) { + dev_err(chg->dev, "fail to select pinctrl active rc=%d\n", + rc); + return rc; + } + + if (chg->ncp3902_en_gpio) { + /* set ncp3902 enable to default output high */ + rc = gpio_direction_output(chg->ncp3902_en_gpio, 1); + if (rc) + pr_err("unable to set output low rc = %d\n", rc); + + /* set ncp3902 pass modess enable to default output low */ + rc = gpio_direction_output(chg->ncp3902_pass_en_gpio, 0); + if (rc) + pr_err("unable to set ncp3902_pass_en_gpio low rc = %d\n", rc); + } + + if (chg->rx_hw_sleep_gpio) { + /* set rx_hw_sleep_gpio to always output high when hlos boot up */ + rc = gpio_direction_output(chg->rx_hw_sleep_gpio, 1); + if (rc) + pr_err("unable to set output high rc = %d\n", rc); + } + + return rc; +} +#endif + #define MICRO_1P5A 1500000 #define MICRO_P1A 100000 #define MICRO_1PA 1000000 @@ -443,6 +648,10 @@ static int smb5_parse_dt_misc(struct smb5 *chip, struct device_node *node) { int rc = 0, byte_len; struct smb_charger *chg = &chip->chg; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int i; + enum of_gpio_flags flags; +#endif of_property_read_u32(node, "qcom,sec-charger-config", &chip->dt.sec_charger_config); @@ -463,11 +672,48 @@ static int smb5_parse_dt_misc(struct smb5 *chip, struct device_node *node) chg->sw_jeita_enabled = of_property_read_bool(node, "qcom,sw-jeita-enable"); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->six_pin_step_charge_enable = of_property_read_bool(node, + "mi,six-pin-step-chg"); + + chg->qc3p5_supported = of_property_read_bool(node, + "mi,support-qc3p5-without-smb"); + + chg->support_ffc = of_property_read_bool(node, + "mi,support-ffc"); +#endif + chg->pd_not_supported = chg->pd_not_supported || of_property_read_bool(node, "qcom,usb-pd-disable"); chg->lpd_disabled = of_property_read_bool(node, "qcom,lpd-disable"); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->use_bq_pump = of_property_read_bool(node, + "mi,use-bq-pump"); + + chg->support_second_ffc_term_current = of_property_read_bool(node, + "mi,support-second-ffc-term-current"); + + chg->support_second_ffc_term_current_diff = SECOND_FFC_TERM_CURRENT_DIFF; + rc = of_property_read_u32(node, "mi,support-second-ffc-term-current-diff", + &chg->support_second_ffc_term_current_diff); + if (rc < 0) + pr_err("read mi,support-second-ffc-term-current-diff failed\n"); + + chg->ext_fg = of_property_read_bool(node, + "qcom,support-ext-fg"); + + chg->ext_bbc = of_property_read_bool(node, + "qcom,support-ext-bbc"); + + chg->support_wireless = of_property_read_bool(node, + "qcom,support-wireless"); + + chg->wireless_bq = of_property_read_bool(node, + "mi,bq-wireless"); +#endif + rc = of_property_read_u32(node, "qcom,wd-bark-time-secs", &chip->dt.wd_bark_time); if (rc < 0 || chip->dt.wd_bark_time < MIN_WD_BARK_TIME) @@ -481,6 +727,160 @@ 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"); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (of_find_property(node, "qcom,thermal-mitigation-dcp", &byte_len)) { + chg->thermal_mitigation_dcp = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_dcp == NULL) + return -ENOMEM; + + chg->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-dcp", + chg->thermal_mitigation_dcp, + chg->thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-mitigation-qc2", &byte_len)) { + chg->thermal_mitigation_qc2 = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_qc2 == NULL) + return -ENOMEM; + + chg->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-qc2", + chg->thermal_mitigation_qc2, + chg->thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-mitigation-pd-base", &byte_len)) { + chg->thermal_mitigation_pd_base = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_pd_base == NULL) + return -ENOMEM; + + chg->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-pd-base", + chg->thermal_mitigation_pd_base, + chg->thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-fcc-qc3-normal", &byte_len)) { + chg->thermal_fcc_qc3_normal = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_fcc_qc3_normal == NULL) + return -ENOMEM; + + chg->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-fcc-qc3-normal", + chg->thermal_fcc_qc3_normal, + chg->thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-fcc-qc3-cp", &byte_len)) { + chg->thermal_fcc_qc3_cp = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_fcc_qc3_cp == NULL) + return -ENOMEM; + + chg->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-fcc-qc3-cp", + chg->thermal_fcc_qc3_cp, + chg->thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-fcc-qc3-classb-cp", &byte_len)) { + chg->thermal_fcc_qc3_classb_cp = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_fcc_qc3_classb_cp == NULL) + return -ENOMEM; + + chg->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-fcc-qc3-classb-cp", + chg->thermal_fcc_qc3_classb_cp, + chg->thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-fcc-pps-bq", &byte_len)) { + chg->thermal_fcc_pps_cp = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_fcc_pps_cp == NULL) + return -ENOMEM; + + chg->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-fcc-pps-bq", + chg->thermal_fcc_pps_cp, + chg->thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-mitigation-icl", &byte_len)) { + chg->thermal_mitigation_icl = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_icl == NULL) + return -ENOMEM; + + chg->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-icl", + chg->thermal_mitigation_icl, + chg->thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } +#endif + if (of_find_property(node, "qcom,thermal-mitigation", &byte_len)) { chg->thermal_mitigation = devm_kzalloc(chg->dev, byte_len, GFP_KERNEL); @@ -500,6 +900,143 @@ static int smb5_parse_dt_misc(struct smb5 *chip, struct device_node *node) } } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->support_wireless) { + if (of_find_property(node, "qcom,thermal-mitigation-dc", &byte_len)) { + chg->thermal_mitigation_dc = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_dc == NULL) + return -ENOMEM; + + chg->dc_thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-dc", + chg->thermal_mitigation_dc, + chg->dc_thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-mitigation-voice", &byte_len)) { + chg->thermal_mitigation_voice = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_voice == NULL) + return -ENOMEM; + + chg->dc_thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-voice", + chg->thermal_mitigation_voice, + chg->dc_thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-mitigation-epp", &byte_len)) { + chg->thermal_mitigation_epp = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_epp == NULL) + return -ENOMEM; + + chg->dc_thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-epp", + chg->thermal_mitigation_epp, + chg->dc_thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-mitigation-bpp-qc3", &byte_len)) { + chg->thermal_mitigation_bpp_qc3 = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_bpp_qc3 == NULL) + return -ENOMEM; + + chg->dc_thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-bpp-qc3", + chg->thermal_mitigation_bpp_qc3, + chg->dc_thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-mitigation-bpp-qc2", &byte_len)) { + chg->thermal_mitigation_bpp_qc2 = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_bpp_qc2 == NULL) + return -ENOMEM; + + chg->dc_thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-bpp-qc2", + chg->thermal_mitigation_bpp_qc2, + chg->dc_thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-mitigation-bpp", &byte_len)) { + chg->thermal_mitigation_bpp = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_bpp == NULL) + return -ENOMEM; + + chg->dc_thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-bpp", + chg->thermal_mitigation_bpp, + chg->dc_thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + + if (of_find_property(node, "qcom,thermal-mitigation-dc-20w", &byte_len)) { + chg->thermal_mitigation_dc_20W = devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + + if (chg->thermal_mitigation_dc_20W == NULL) + return -ENOMEM; + + chg->dc_thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "qcom,thermal-mitigation-dc-20w", + chg->thermal_mitigation_dc_20W, + chg->dc_thermal_levels); + if (rc < 0) { + dev_err(chg->dev, + "Couldn't read threm limits rc = %d\n", rc); + return rc; + } + } + } +#endif + rc = of_property_read_u32(node, "qcom,charger-temp-max", &chg->charger_temp_max); if (rc < 0) @@ -578,6 +1115,47 @@ 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"); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->qc_class_ab = of_property_read_bool(node, + "qcom,distinguish-qc-class-ab"); + if (chg->wireless_bq) + smb5_charge_gpio_init(chg, node); + + if (chg->six_pin_step_charge_enable) { + rc = smb5_charge_step_charge_init(chg, node); + if (!rc) { + for (i = 0; i < MAX_STEP_ENTRIES; i++) + pr_err("six-pin-step-chg-cfg: %duV, %duA\n", + chg->six_pin_step_cfg[i].vfloat_step_uv, + chg->six_pin_step_cfg[i].fcc_step_ua); + } + rc = of_property_read_u32(node, "mi,six-pin-soc-th", &chg->step_soc_threshold); + if (rc < 0) + pr_err("read six-pin-soc-threshold failed\n"); + } + + chg->chg_warm_threshold = BATT_WARM_THRESHOLD; + rc = of_property_read_u32(node, "qcom,chg-warm-th", &chg->chg_warm_threshold); + if (rc < 0) + pr_err("read chg-warm-threshold failed\n"); + + chg->chg_cool_threshold = BATT_COOL_THRESHOLD; + rc = of_property_read_u32(node, "qcom,chg-cool-th", &chg->chg_cool_threshold); + if (rc < 0) + pr_err("read chg-cool-threshold failed\n"); + + chg->support_conn_therm = of_property_read_bool(node, + "qcom,support-conn-therm"); + + if (chg->support_conn_therm) { + chg->vbus_disable_gpio = of_get_named_gpio_flags(node, + "vbus-disable-gpio", 0, &flags); + if (chg->vbus_disable_gpio < 0) { + pr_err("failed to vbus disable gpio flags\n"); + } + } +#endif + of_property_read_u32(node, "qcom,fcc-step-delay-ms", &chg->chg_param.fcc_step_delay_ms); if (chg->chg_param.fcc_step_delay_ms <= 0) @@ -700,24 +1278,45 @@ static int smb5_parse_dt_currents(struct smb5 *chip, struct device_node *node) rc = of_property_read_u32(node, "qcom,chg-term-current-ma", &chip->dt.term_current_thresh_hi_ma); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->chg_term_current_thresh_hi_from_dts = chip->dt.term_current_thresh_hi_ma; +#endif + chg->wls_icl_ua = DCIN_ICL_MAX_UA; rc = of_property_read_u32(node, "qcom,wls-current-max-ua", &tmp); if (!rc && tmp < DCIN_ICL_MAX_UA) chg->wls_icl_ua = tmp; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + rc = of_property_read_u32(node, + "mi,fcc-batt-unverify-ua", &chip->dt.batt_unverify_fcc_ua); + if (rc < 0) + chip->dt.batt_unverify_fcc_ua = -EINVAL; +#endif + return 0; } static int smb5_parse_dt_voltages(struct smb5 *chip, struct device_node *node) { int rc = 0; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + struct smb_charger *chg = &chip->chg; +#endif rc = of_property_read_u32(node, "qcom,fv-max-uv", &chip->dt.batt_profile_fv_uv); if (rc < 0) chip->dt.batt_profile_fv_uv = -EINVAL; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + rc = of_property_read_u32(node, + "qcom,non-fcc-fv-max-uv", &chip->dt.non_fcc_batt_profile_fv_uv); + if (rc < 0) + chip->dt.non_fcc_batt_profile_fv_uv = -EINVAL; +#endif + rc = of_property_read_u32(node, "qcom,chg-inhibit-threshold-mv", &chip->dt.chg_inhibit_thr_mv); if (!rc && (chip->dt.chg_inhibit_thr_mv < 0 || @@ -733,6 +1332,9 @@ static int smb5_parse_dt_voltages(struct smb5 *chip, struct device_node *node) pr_err("qcom,auto-recharge-vbat-mv is incorrect\n"); return -EINVAL; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->auto_recharge_vbat = chip->dt.auto_recharge_vbat_mv; +#endif return 0; } @@ -850,6 +1452,9 @@ static enum power_supply_property smb5_usb_props[] = { POWER_SUPPLY_PROP_TYPE, POWER_SUPPLY_PROP_TYPEC_MODE, POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_TYPEC_BOOST_OTG_DISABLE, +#endif POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION, POWER_SUPPLY_PROP_LOW_POWER, POWER_SUPPLY_PROP_PD_ACTIVE, @@ -860,10 +1465,18 @@ static enum power_supply_property smb5_usb_props[] = { POWER_SUPPLY_PROP_CTM_CURRENT_MAX, POWER_SUPPLY_PROP_HW_CURRENT_MAX, POWER_SUPPLY_PROP_REAL_TYPE, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_HVDCP3_TYPE, + POWER_SUPPLY_PROP_QUICK_CHARGE_TYPE, +#endif POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, POWER_SUPPLY_PROP_PD_VOLTAGE_MIN, POWER_SUPPLY_PROP_CONNECTOR_TYPE, POWER_SUPPLY_PROP_CONNECTOR_HEALTH, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_CONNECTOR_TEMP, + POWER_SUPPLY_PROP_VBUS_DISABLE, +#endif POWER_SUPPLY_PROP_VOLTAGE_MAX, POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MAX_LIMIT, @@ -876,9 +1489,16 @@ static enum power_supply_property smb5_usb_props[] = { POWER_SUPPLY_PROP_QC_OPTI_DISABLE, POWER_SUPPLY_PROP_VOLTAGE_VPH, POWER_SUPPLY_PROP_THERM_ICL_LIMIT, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_FASTCHARGE_MODE, + POWER_SUPPLY_PROP_PD_AUTHENTICATION, +#endif POWER_SUPPLY_PROP_SKIN_HEALTH, POWER_SUPPLY_PROP_APSD_RERUN, POWER_SUPPLY_PROP_APSD_TIMEOUT, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_APDO_MAX, +#endif POWER_SUPPLY_PROP_CHARGER_STATUS, POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED, }; @@ -927,6 +1547,34 @@ static int smb5_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_REAL_TYPE: val->intval = chg->real_charger_type; break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_HVDCP3_TYPE: + if (chg->real_charger_type != POWER_SUPPLY_TYPE_USB_HVDCP_3 + && chg->real_charger_type != POWER_SUPPLY_TYPE_USB_HVDCP_3P5) + val->intval = HVDCP3_NONE; /* 0: none hvdcp3 insert */ + else { + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3P5) { + if (chg->qc3p5_power_limit_w == 18) + val->intval = HVDCP3P5_CLASSA_18W; + else if (chg->qc3p5_power_limit_w == 27) + val->intval = HVDCP3P5_CLASSB_27W; + else + val->intval = HVDCP3_NONE; + } else { + if (chg->is_qc_class_a) + val->intval = HVDCP3_CLASSA_18W; /* 18W hvdcp3 insert */ + else if (chg->is_qc_class_b) + val->intval = HVDCP3_CLASSB_27W; /* 27W hvdcp3 insert */ + else + val->intval = HVDCP3_NONE; + } + } + break; + case POWER_SUPPLY_PROP_QUICK_CHARGE_TYPE: + val->intval = smblib_get_quick_charge_type(chg); + pr_err("quick charge type is %d\n", val->intval); + break; +#endif case POWER_SUPPLY_PROP_TYPEC_MODE: rc = smblib_get_usb_prop_typec_mode(chg, val); break; @@ -988,6 +1636,17 @@ static int smb5_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CONNECTOR_HEALTH: val->intval = smblib_get_prop_connector_health(chg); break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_CONNECTOR_TEMP: + if (chg->fake_conn_temp != 0) + val->intval = chg->fake_conn_temp; + else + val->intval = smblib_get_prop_connector_temp(chg); + break; + case POWER_SUPPLY_PROP_VBUS_DISABLE: + val->intval = chg->vbus_disable; + break; +#endif case POWER_SUPPLY_PROP_SCOPE: rc = smblib_get_prop_scope(chg, val); break; @@ -1019,6 +1678,14 @@ static int smb5_usb_get_prop(struct power_supply *psy, val->intval = get_client_vote(chg->usb_icl_votable, THERMAL_THROTTLE_VOTER); break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_FASTCHARGE_MODE: + val->intval = smblib_get_fastcharge_mode(chg); + break; + case POWER_SUPPLY_PROP_PD_AUTHENTICATION: + val->intval = chg->pd_verifed; + break; +#endif case POWER_SUPPLY_PROP_ADAPTER_CC_MODE: val->intval = chg->adapter_cc_mode; break; @@ -1031,6 +1698,11 @@ static int smb5_usb_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_APSD_TIMEOUT: val->intval = chg->apsd_ext_timeout; break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_APDO_MAX: + val->intval = chg->apdo_max; + break; +#endif case POWER_SUPPLY_PROP_CHARGER_STATUS: val->intval = 0; if (chg->sdam_base) { @@ -1073,12 +1745,22 @@ static int smb5_usb_set_prop(struct power_supply *psy, int icl, rc = 0; switch (psp) { +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_FAKE_HVDCP3: + chg->fake_hvdcp3 = val->intval; + break; +#endif case POWER_SUPPLY_PROP_PD_CURRENT_MAX: rc = smblib_set_prop_pd_current_max(chg, val); break; case POWER_SUPPLY_PROP_TYPEC_POWER_ROLE: rc = smblib_set_prop_typec_power_role(chg, val); break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_TYPEC_BOOST_OTG_DISABLE: + rc = smblib_set_prop_typec_boost_otg_disable(chg, val); + break; +#endif case POWER_SUPPLY_PROP_TYPEC_SRC_RP: rc = smblib_set_prop_typec_select_rp(chg, val); break; @@ -1114,6 +1796,17 @@ static int smb5_usb_set_prop(struct power_supply *psy, chg->connector_health = val->intval; power_supply_changed(chg->usb_psy); break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_CONNECTOR_TEMP: + chg->fake_conn_temp = val->intval; + break; + case POWER_SUPPLY_PROP_VBUS_DISABLE: + if (chg->support_conn_therm){ + smblib_set_vbus_disable(chg, val->intval); + } else + chg->vbus_disable = val->intval; + break; +#endif case POWER_SUPPLY_PROP_THERM_ICL_LIMIT: if (!is_client_vote_enabled(chg->usb_icl_votable, THERMAL_THROTTLE_VOTER)) { @@ -1135,6 +1828,25 @@ static int smb5_usb_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_MAX_LIMIT: smblib_set_prop_usb_voltage_max_limit(chg, val); break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_PD_AUTHENTICATION: + chg->pd_verifed = val->intval; + pr_err("set pd_verifed =%d\n", chg->pd_verifed ); + /*if set pd authentication auto set fastcharge mode*/ + /*do not break here*/ + case POWER_SUPPLY_PROP_FASTCHARGE_MODE: + rc = smblib_set_fastcharge_mode(chg, val->intval); + power_supply_changed(chg->usb_psy); + schedule_delayed_work(&chg->report_soc_decimal_work, + msecs_to_jiffies(REPORT_SOC_DECIMAL_MS)); + if (chg->ext_fg && chg->step_chg_enabled) + schedule_delayed_work(&chg->step_charge_notify_work, + msecs_to_jiffies(2000)); + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + smblib_set_prop_input_current_max(chg, val); + break; +#endif case POWER_SUPPLY_PROP_ADAPTER_CC_MODE: chg->adapter_cc_mode = val->intval; break; @@ -1143,6 +1855,11 @@ static int smb5_usb_set_prop(struct power_supply *psy, chg->apsd_ext_timeout = false; smblib_rerun_apsd(chg); break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_APDO_MAX: + chg->apdo_max = val->intval; + break; +#endif default: pr_err("set prop %d is not supported\n", psp); rc = -EINVAL; @@ -1158,10 +1875,21 @@ static int smb5_usb_prop_is_writeable(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_CTM_CURRENT_MAX: case POWER_SUPPLY_PROP_CONNECTOR_HEALTH: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_CONNECTOR_TEMP: + case POWER_SUPPLY_PROP_VBUS_DISABLE: +#endif case POWER_SUPPLY_PROP_THERM_ICL_LIMIT: case POWER_SUPPLY_PROP_VOLTAGE_MAX_LIMIT: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_FASTCHARGE_MODE: + case POWER_SUPPLY_PROP_PD_AUTHENTICATION: +#endif case POWER_SUPPLY_PROP_ADAPTER_CC_MODE: case POWER_SUPPLY_PROP_APSD_RERUN: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_APDO_MAX: +#endif return 1; default: break; @@ -1185,10 +1913,17 @@ static int smb5_init_usb_psy(struct smb5 *chip) struct power_supply_config usb_cfg = {}; struct smb_charger *chg = &chip->chg; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->usb_psy_desc = usb_psy_desc; +#endif usb_cfg.drv_data = chip; usb_cfg.of_node = chg->dev->of_node; chg->usb_psy = devm_power_supply_register(chg->dev, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + &chg->usb_psy_desc, +#else &usb_psy_desc, +#endif &usb_cfg); if (IS_ERR(chg->usb_psy)) { pr_err("Couldn't register USB power supply\n"); @@ -1221,6 +1956,12 @@ static int smb5_usb_port_get_prop(struct power_supply *psy, val->intval = POWER_SUPPLY_TYPE_USB; break; case POWER_SUPPLY_PROP_ONLINE: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->report_input_absent) { + val->intval = 0; + break; + } +#endif rc = smblib_get_prop_usb_online(chg, val); if (!val->intval) break; @@ -1511,7 +2252,9 @@ static int smb5_usb_main_prop_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_FORCE_MAIN_FCC: case POWER_SUPPLY_PROP_FORCE_MAIN_ICL: case POWER_SUPPLY_PROP_COMP_CLAMP_LEVEL: +#ifndef CONFIG_MACH_XIAOMI_SM8250 case POWER_SUPPLY_PROP_HOT_TEMP: +#endif rc = 1; break; default: @@ -1650,7 +2393,9 @@ static int smb5_dc_prop_is_writeable(struct power_supply *psy, { switch (psp) { case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: +#ifndef CONFIG_MACH_XIAOMI_SM8250 case POWER_SUPPLY_PROP_CURRENT_MAX: +#endif return 1; default: break; @@ -1687,11 +2432,546 @@ static int smb5_init_dc_psy(struct smb5 *chip) return 0; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static int smb5_get_prop_input_voltage_regulation(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) { + chg->wls_chip_psy = chg->idtp_psy; + } else { + chg->wip_psy = power_supply_get_by_name("rx1619"); + if (chg->wip_psy) + chg->wls_chip_psy = chg->wip_psy; + else + return -EINVAL; + } + + //msleep(200); + if (chg->wls_chip_psy) + rc = power_supply_get_property(chg->wls_chip_psy, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, val); + + val->intval = 1000*val->intval;//IDT in mV + + return rc; +} + +static int smb5_get_prop_input_voltage_vrect(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) { + chg->wls_chip_psy = chg->idtp_psy; + } else { + chg->wip_psy = power_supply_get_by_name("rx1619"); + if (chg->wip_psy) + chg->wls_chip_psy = chg->wip_psy; + else + return -EINVAL; + } + + //msleep(200); + + if (chg->wls_chip_psy) + rc = power_supply_get_property(chg->wls_chip_psy, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_VRECT, val); + + val->intval = 1000*val->intval;//IDT in mV + + return rc; +} + +static int smb5_get_prop_rx_iout(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) { + chg->wls_chip_psy = chg->idtp_psy; + } else { + chg->wip_psy = power_supply_get_by_name("rx1619"); + if (chg->wip_psy) + chg->wls_chip_psy = chg->wip_psy; + else + return -EINVAL; + } + //msleep(100); + if (chg->wls_chip_psy) + rc = power_supply_get_property(chg->wls_chip_psy, + POWER_SUPPLY_PROP_RX_IOUT, val); + + val->intval = 1000*val->intval;//IDT in mV + + return rc; +} + +static int smb5_get_prop_wireless_signal(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) { + chg->wls_chip_psy = chg->idtp_psy; + } else { + chg->wip_psy = power_supply_get_by_name("rx1619"); + if (chg->wip_psy) + chg->wls_chip_psy = chg->wip_psy; + else + return -EINVAL; + } + + if (chg->wls_chip_psy) + rc = power_supply_get_property(chg->wls_chip_psy, + POWER_SUPPLY_PROP_SIGNAL_STRENGTH, val); + + return rc; +} + +static int smb5_set_prop_input_voltage_regulation(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc; + + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) { + chg->wls_chip_psy = chg->idtp_psy; + } else { + chg->wip_psy = power_supply_get_by_name("rx1619"); + if (chg->wip_psy) + chg->wls_chip_psy = chg->wip_psy; + else + return -EINVAL; + } + + if (chg->wls_chip_psy) + rc = power_supply_set_property(chg->wls_chip_psy, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, val); + + return rc; +} + +static int smb5_get_prop_wirless_type(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc = 0; + + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) + chg->wls_chip_psy = chg->idtp_psy; + else { + chg->wip_psy = power_supply_get_by_name("rx1619"); + if (chg->wip_psy) + chg->wls_chip_psy = chg->wip_psy; + else + return -EINVAL; + } + + if (chg->wls_chip_psy) + rc = power_supply_get_property(chg->wls_chip_psy, + POWER_SUPPLY_PROP_TX_ADAPTER, val); + + return rc; +} + +/*set mode of DIV 2*/ +static int smb5_set_prop_div2_mode(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc; + + dev_info(chg->dev, "%s: set mode is = %d\n", + __func__, val->intval); + + chg->ln_psy = power_supply_get_by_name("lionsemi"); + //chg->halo_psy = power_supply_get_by_name("halo"); + if (chg->ln_psy) + chg->cp_chip_psy = chg->ln_psy; + /*else if (chg->halo_psy) + chg->cp_chip_psy = chg->halo_psy;*/ + else + return -EINVAL; + + if (chg->cp_chip_psy) + rc = power_supply_set_property(chg->cp_chip_psy, + POWER_SUPPLY_PROP_DIV_2_MODE, val); + + return rc; +} + +static int smb5_get_prop_div2_mode(struct smb_charger *chg, + union power_supply_propval *val) +{ + dev_info(chg->dev, "%s: get div2 mode\n", __func__); + + chg->ln_psy = power_supply_get_by_name("lionsemi"); + //chg->halo_psy = power_supply_get_by_name("halo"); + + if (chg->ln_psy) + chg->cp_chip_psy = chg->ln_psy; + /*else if (chg->halo_psy) + chg->cp_chip_psy = chg->halo_psy;*/ + else + return -EINVAL; + + if (chg->cp_chip_psy) { + power_supply_get_property(chg->cp_chip_psy, + POWER_SUPPLY_PROP_DIV_2_MODE, val); + dev_info(chg->dev, "%s: get mode is = %d\n", + __func__, val->intval); + } + + return 1; +} +static int smb5_set_prop_reverse_chg_mode(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc; + + dev_info(chg->dev, "%s: set mode is = %d\n", + __func__, val->intval); + + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) { + chg->wls_chip_psy = chg->idtp_psy; + } else { + chg->wip_psy = power_supply_get_by_name("rx1619"); + if (chg->wip_psy) + chg->wls_chip_psy = chg->wip_psy; + else + return -EINVAL; + } + + if (chg->wls_chip_psy) + rc = power_supply_set_property(chg->wls_chip_psy, + POWER_SUPPLY_PROP_REVERSE_CHG_MODE, val); + + return rc; +} + +static int smb5_set_prop_otg_mode(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc; + + dev_info(chg->dev, "%s: set mode is = %d\n", + __func__, val->intval); + + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) { + chg->wls_chip_psy = chg->idtp_psy; + } else { + chg->wip_psy = power_supply_get_by_name("rx1619"); + if (chg->wip_psy) + chg->wls_chip_psy = chg->wip_psy; + else + return -EINVAL; + } + + if (chg->wls_chip_psy) + rc = power_supply_set_property(chg->wls_chip_psy, + POWER_SUPPLY_PROP_OTG_STATE, val); + + return rc; +} + +static int smb5_get_prop_reverse_chg_mode(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) { + chg->wls_chip_psy = chg->idtp_psy; + } else { + chg->wip_psy = power_supply_get_by_name("rx1619"); + if (chg->wip_psy) + chg->wls_chip_psy = chg->wip_psy; + else + return -EINVAL; + } + + if (chg->wls_chip_psy) { + rc = power_supply_get_property(chg->wls_chip_psy, + POWER_SUPPLY_PROP_REVERSE_CHG_MODE, val); + dev_info(chg->dev, "%s: get mode is = %d\n", + __func__, val->intval); + } + + return rc; +} + +static int smb5_get_prop_wirless_chip_ok(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) { + chg->wls_chip_psy = chg->idtp_psy; + } else { + chg->wip_psy = power_supply_get_by_name("rx1619"); + if (chg->wip_psy) + chg->wls_chip_psy = chg->wip_psy; + else + return -EINVAL; + } + + if (chg->wls_chip_psy) { + rc = power_supply_get_property(chg->wls_chip_psy, + POWER_SUPPLY_PROP_CHIP_OK, val); + dev_info(chg->dev, "%s: get chip status is = %d\n", + __func__, val->intval); + } + + return rc; +} + +int smblib_enable_aicl(struct smb_charger *chg, int enable) +{ + int rc = 0; + u8 mask = 0; + u8 val = 0; + + mask = USBIN_AICL_EN_BIT; + val = enable?USBIN_AICL_EN_BIT:0; + + rc = smblib_masked_write(chg, USBIN_AICL_OPTIONS_CFG_REG, + mask, val); + if (rc < 0) { + dev_err(chg->dev, "Couldn't config AICL enable rc=%d\n", rc); + return rc; + } + return 0; + +} +/************************* + * WIRELESS PSY REGISTRATION * + *************************/ + +static enum power_supply_property smb5_wireless_props[] = { + POWER_SUPPLY_PROP_WIRELESS_VERSION, + POWER_SUPPLY_PROP_SIGNAL_STRENGTH, + POWER_SUPPLY_PROP_WIRELESS_WAKELOCK, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_VRECT, + POWER_SUPPLY_PROP_RX_IOUT, + POWER_SUPPLY_PROP_WIRELESS_CP_EN, + POWER_SUPPLY_PROP_WIRELESS_POWER_GOOD_EN, + POWER_SUPPLY_PROP_SW_DISABLE_DC_EN, + POWER_SUPPLY_PROP_TX_ADAPTER, + POWER_SUPPLY_PROP_TX_MAC, + POWER_SUPPLY_PROP_DC_RESET, + POWER_SUPPLY_PROP_DIV_2_MODE, + POWER_SUPPLY_PROP_REVERSE_CHG_MODE, + POWER_SUPPLY_PROP_REVERSE_CHG_STATE, + POWER_SUPPLY_PROP_REVERSE_GPIO_STATE, + POWER_SUPPLY_PROP_AICL_ENABLE, + POWER_SUPPLY_PROP_OTG_STATE, + POWER_SUPPLY_PROP_WIRELESS_FW_VERSION, + POWER_SUPPLY_PROP_CHIP_OK, +}; + +static int smb5_wireless_set_prop(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct smb5 *chip = power_supply_get_drvdata(psy); + struct smb_charger *chg = &chip->chg; + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_WIRELESS_VERSION: + dev_info(chg->dev, "set version=%d\n", val->intval); + break; + case POWER_SUPPLY_PROP_WIRELESS_WAKELOCK: + rc = smblib_set_prop_wireless_wakelock(chg, val); + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: + smb5_set_prop_input_voltage_regulation(chg, val); + break; + case POWER_SUPPLY_PROP_WIRELESS_CP_EN: + smblib_set_wirless_cp_enable(chg, val); + break; + case POWER_SUPPLY_PROP_WIRELESS_POWER_GOOD_EN: + smblib_set_wirless_power_good_enable(chg, val); + break; + case POWER_SUPPLY_PROP_SW_DISABLE_DC_EN: + smblib_set_sw_disable_dc_en(chg, val); + break; + case POWER_SUPPLY_PROP_DC_RESET: + rc = smblib_set_prop_dc_reset(chg); + break; + case POWER_SUPPLY_PROP_DIV_2_MODE: + rc = smb5_set_prop_div2_mode(chg, val); + break; + case POWER_SUPPLY_PROP_REVERSE_CHG_MODE: + rc = smb5_set_prop_reverse_chg_mode(chg, val); + break; + case POWER_SUPPLY_PROP_REVERSE_CHG_STATE: + chg->reverse_chg_state = val->intval; + break; + case POWER_SUPPLY_PROP_AICL_ENABLE: + rc = smblib_enable_aicl(chg, val->intval); + break; + case POWER_SUPPLY_PROP_REVERSE_GPIO_STATE: + chg->reverse_gpio_state = val->intval; + break; + case POWER_SUPPLY_PROP_OTG_STATE: + rc = smb5_set_prop_otg_mode(chg, val); + break; + default: + return -EINVAL; + } + + return rc; +} + +static int smb5_wireless_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct smb5 *chip = power_supply_get_drvdata(psy); + struct smb_charger *chg = &chip->chg; + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_WIRELESS_VERSION: + rc = smblib_get_prop_wireless_version(chg, val); + break; + case POWER_SUPPLY_PROP_WIRELESS_FW_VERSION: + rc = smblib_get_prop_wireless_fw_version(chg, val); + break; + case POWER_SUPPLY_PROP_SIGNAL_STRENGTH: + smb5_get_prop_wireless_signal(chg, val); + break; + case POWER_SUPPLY_PROP_WIRELESS_WAKELOCK: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: + smb5_get_prop_input_voltage_regulation(chg, val); + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_VRECT: + smb5_get_prop_input_voltage_vrect(chg, val); + break; + case POWER_SUPPLY_PROP_RX_IOUT: + smb5_get_prop_rx_iout(chg, val); + break; + case POWER_SUPPLY_PROP_WIRELESS_CP_EN: + if (chg->wireless_bq) + val->intval = chg->en_bq_flag; + else + val->intval = chg->flag_dc_present; + break; + case POWER_SUPPLY_PROP_WIRELESS_POWER_GOOD_EN: + val->intval = chg->power_good_en; + break; + case POWER_SUPPLY_PROP_TX_ADAPTER: + smb5_get_prop_wirless_type(chg, val); + break; + case POWER_SUPPLY_PROP_DC_RESET: + val->intval = 0; + break; + case POWER_SUPPLY_PROP_DIV_2_MODE: + smb5_get_prop_div2_mode(chg, val); + break; + case POWER_SUPPLY_PROP_SW_DISABLE_DC_EN: + val->intval = 0; + break; + case POWER_SUPPLY_PROP_REVERSE_CHG_MODE: + smb5_get_prop_reverse_chg_mode(chg, val); + break; + case POWER_SUPPLY_PROP_REVERSE_CHG_STATE: + val->intval = chg->reverse_chg_state; + break; + case POWER_SUPPLY_PROP_REVERSE_GPIO_STATE: + val->intval = chg->reverse_gpio_state; + break; + case POWER_SUPPLY_PROP_AICL_ENABLE: + val->intval = 0; + break; + case POWER_SUPPLY_PROP_CHIP_OK: + smb5_get_prop_wirless_chip_ok(chg, val); + break; + case POWER_SUPPLY_PROP_OTG_STATE: + val->intval = 0; + break; + + default: + return -EINVAL; + } + if (rc < 0) { + pr_debug("Couldn't get prop %d rc = %d\n", psp, rc); + return -ENODATA; + } + return 0; +} + +static int smb5_wireless_prop_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_WIRELESS_VERSION: + case POWER_SUPPLY_PROP_WIRELESS_WAKELOCK: + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: + case POWER_SUPPLY_PROP_WIRELESS_CP_EN: + case POWER_SUPPLY_PROP_WIRELESS_POWER_GOOD_EN: + case POWER_SUPPLY_PROP_SW_DISABLE_DC_EN: + case POWER_SUPPLY_PROP_DIV_2_MODE: + case POWER_SUPPLY_PROP_REVERSE_CHG_MODE: + case POWER_SUPPLY_PROP_REVERSE_CHG_STATE: + case POWER_SUPPLY_PROP_CURRENT_MAX: + return 1; + default: + break; + } + + return 0; +} + +static const struct power_supply_desc wireless_psy_desc = { + .name = "wireless", + .type = POWER_SUPPLY_TYPE_WIRELESS, + .properties = smb5_wireless_props, + .num_properties = ARRAY_SIZE(smb5_wireless_props), + .get_property = smb5_wireless_get_prop, + .set_property = smb5_wireless_set_prop, + .property_is_writeable = smb5_wireless_prop_is_writeable, +}; + +static int smb5_init_wireless_psy(struct smb5 *chip) +{ + struct power_supply_config wireless_cfg = {}; + struct smb_charger *chg = &chip->chg; + + wireless_cfg.drv_data = chip; + wireless_cfg.of_node = chg->dev->of_node; + chg->wireless_psy = power_supply_register(chg->dev, + &wireless_psy_desc, + &wireless_cfg); + if (IS_ERR(chg->wireless_psy)) { + pr_err("Couldn't register wireless power supply\n"); + return PTR_ERR(chg->wireless_psy); + } + + return 0; +} +#endif + /************************* * BATT PSY REGISTRATION * *************************/ static enum power_supply_property smb5_batt_props[] = { POWER_SUPPLY_PROP_INPUT_SUSPEND, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_BATTERY_INPUT_SUSPEND, +#endif POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_PRESENT, @@ -1716,6 +2996,9 @@ static enum power_supply_property smb5_batt_props[] = { POWER_SUPPLY_PROP_PARALLEL_DISABLE, POWER_SUPPLY_PROP_SET_SHIP_MODE, POWER_SUPPLY_PROP_DIE_HEALTH, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_DC_THERMAL_LEVELS, +#endif POWER_SUPPLY_PROP_RERUN_AICL, POWER_SUPPLY_PROP_DP_DM, POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, @@ -1723,11 +3006,23 @@ static enum power_supply_property smb5_batt_props[] = { POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_RECHARGE_SOC, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_RECHARGE_VBAT, + POWER_SUPPLY_PROP_NIGHT_CHARGING, +#endif POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_FORCE_RECHARGE, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, + POWER_SUPPLY_PROP_DP_DM_BQ, + POWER_SUPPLY_PROP_TYPE_RECHECK, + POWER_SUPPLY_PROP_WARM_FAKE_CHARGING, + POWER_SUPPLY_PROP_STEP_VFLOAT_INDEX, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, +#endif }; #define DEBUG_ACCESSORY_TEMP_DECIDEGC 250 @@ -1751,15 +3046,34 @@ static int smb5_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_INPUT_SUSPEND: rc = smblib_get_prop_input_suspend(chg, val); break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_BATTERY_INPUT_SUSPEND: + rc = smblib_get_prop_battery_input_suspend(chg, val); + break; +#endif case POWER_SUPPLY_PROP_CHARGE_TYPE: rc = smblib_get_prop_batt_charge_type(chg, val); break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + if (chg->ext_fg) + rc = smblib_get_prop_from_bms(chg, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, val); + else + rc = smblib_get_prop_batt_capacity_level(chg, val); + break; +#endif case POWER_SUPPLY_PROP_CAPACITY: rc = smblib_get_prop_batt_capacity(chg, val); break; case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: rc = smblib_get_prop_system_temp_level(chg, val); break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_DC_THERMAL_LEVELS: + rc = smblib_get_prop_dc_temp_level(chg, val); + break; +#endif case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: rc = smblib_get_prop_system_temp_level_max(chg, val); break; @@ -1818,7 +3132,11 @@ static int smb5_batt_get_prop(struct power_supply *psy, POWER_SUPPLY_PROP_TEMP, val); break; case POWER_SUPPLY_PROP_TECHNOLOGY: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO; +#else val->intval = POWER_SUPPLY_TECHNOLOGY_LION; +#endif break; case POWER_SUPPLY_PROP_CHARGE_DONE: rc = smblib_get_prop_batt_charge_done(chg, val); @@ -1851,6 +3169,11 @@ static int smb5_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_RECHARGE_SOC: val->intval = chg->auto_recharge_soc; break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_RECHARGE_VBAT: + val->intval = chg->auto_recharge_vbat; + break; +#endif case POWER_SUPPLY_PROP_CHARGE_QNOVO_ENABLE: val->intval = 0; if (!chg->qnovo_disable_votable) @@ -1880,6 +3203,26 @@ static int smb5_batt_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE: val->intval = chg->fcc_stepper_enable; break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED: + rc = smblib_get_prop_battery_charging_enabled(chg, val); + break; + case POWER_SUPPLY_PROP_DP_DM_BQ: + val->intval = chg->pulse_cnt; + break; + case POWER_SUPPLY_PROP_TYPE_RECHECK: + rc = smblib_get_prop_type_recheck(chg, val); + break; + case POWER_SUPPLY_PROP_WARM_FAKE_CHARGING: + val->intval = chg->warm_fake_charging; + break; + case POWER_SUPPLY_PROP_STEP_VFLOAT_INDEX: + val->intval = chg->index_vfloat; + break; + case POWER_SUPPLY_PROP_NIGHT_CHARGING: + rc = smblib_night_charging_func(chg, val); + break; +#endif default: pr_err("batt power supply prop %d not supported\n", psp); return -EINVAL; @@ -1907,9 +3250,20 @@ static int smb5_batt_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_INPUT_SUSPEND: rc = smblib_set_prop_input_suspend(chg, val); break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_BATTERY_INPUT_SUSPEND: + rc = smblib_set_prop_battery_input_suspend(chg, val); + break; +#endif case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: rc = smblib_set_prop_system_temp_level(chg, val); break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_DC_THERMAL_LEVELS: + if (chg->support_wireless) + rc = smblib_set_prop_dc_temp_level(chg, val); + break; +#endif case POWER_SUPPLY_PROP_CAPACITY: rc = smblib_set_prop_batt_capacity(chg, val); break; @@ -1933,6 +3287,11 @@ static int smb5_batt_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: chg->step_chg_enabled = !!val->intval; break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_SW_JEITA_ENABLED: + chg->sw_jeita_enabled = !!val->intval; + break; +#endif case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: chg->batt_profile_fcc_ua = val->intval; vote(chg->fcc_votable, BATT_PROFILE_VOTER, true, val->intval); @@ -1975,6 +3334,11 @@ static int smb5_batt_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_RECHARGE_SOC: rc = smblib_set_prop_rechg_soc_thresh(chg, val); break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_RECHARGE_VBAT: + rc = smblib_set_prop_rechg_vbat_thresh(chg, val); + break; +#endif case POWER_SUPPLY_PROP_FORCE_RECHARGE: /* toggle charging to force recharge */ vote(chg->chg_disable_votable, FORCE_RECHARGE_VOTER, @@ -1987,6 +3351,24 @@ static int smb5_batt_set_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE: chg->fcc_stepper_enable = val->intval; break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED: + rc = smblib_set_prop_battery_charging_enabled(chg, val); + break; + case POWER_SUPPLY_PROP_DP_DM_BQ: + if (chg->use_bq_pump) + rc = smblib_dp_dm_bq(chg, val->intval); + break; + case POWER_SUPPLY_PROP_TYPE_RECHECK: + rc = smblib_set_prop_type_recheck(chg, val); + break; + case POWER_SUPPLY_PROP_WARM_FAKE_CHARGING: + chg->warm_fake_charging = val->intval; + break; + case POWER_SUPPLY_PROP_NIGHT_CHARGING: + chg->night_chg_flag = val->intval; + break; +#endif default: rc = -EINVAL; } @@ -2000,6 +3382,9 @@ static int smb5_batt_prop_is_writeable(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_STATUS: case POWER_SUPPLY_PROP_INPUT_SUSPEND: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_BATTERY_INPUT_SUSPEND: +#endif case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: case POWER_SUPPLY_PROP_CAPACITY: case POWER_SUPPLY_PROP_PARALLEL_DISABLE: @@ -2007,7 +3392,19 @@ static int smb5_batt_prop_is_writeable(struct power_supply *psy, case POWER_SUPPLY_PROP_RERUN_AICL: case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_SW_JEITA_ENABLED: +#endif case POWER_SUPPLY_PROP_DIE_HEALTH: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED: + case POWER_SUPPLY_PROP_DP_DM_BQ: + case POWER_SUPPLY_PROP_TYPE_RECHECK: + case POWER_SUPPLY_PROP_DC_THERMAL_LEVELS: + case POWER_SUPPLY_PROP_WARM_FAKE_CHARGING: + case POWER_SUPPLY_PROP_RECHARGE_VBAT: + case POWER_SUPPLY_PROP_NIGHT_CHARGING: +#endif return 1; default: break; @@ -2176,6 +3573,7 @@ static int smb5_configure_typec(struct smb_charger *chg) smblib_apsd_enable(chg, true); +#ifndef CONFIG_MACH_XIAOMI_SM8250 rc = smblib_read(chg, TYPE_C_SNK_STATUS_REG, &val); if (rc < 0) { dev_err(chg->dev, "failed to read TYPE_C_SNK_STATUS_REG rc=%d\n", @@ -2185,6 +3583,7 @@ static int smb5_configure_typec(struct smb_charger *chg) } if (!(val & SNK_DAM_MASK)) { +#endif rc = smblib_masked_write(chg, TYPE_C_CFG_REG, BC1P2_START_ON_CC_BIT, 0); if (rc < 0) { @@ -2193,7 +3592,9 @@ static int smb5_configure_typec(struct smb_charger *chg) return rc; } +#ifndef CONFIG_MACH_XIAOMI_SM8250 } +#endif /* Use simple write to clear interrupts */ rc = smblib_write(chg, TYPE_C_INTERRUPT_EN_CFG_1_REG, 0); @@ -2363,10 +3764,22 @@ static int smb5_configure_iterm_thresholds_adc(struct smb5 *chip) max_limit_ma); raw_hi_thresh = sign_extend32(raw_hi_thresh, 15); buf = (u8 *)&raw_hi_thresh; +#ifndef CONFIG_MACH_XIAOMI_SM8250 raw_hi_thresh = buf[1] | (buf[0] << 8); +#endif +#ifdef CONFIG_MACH_XIAOMI_SM8250 + rc = smblib_write(chg, CHGR_ADC_ITERM_UP_THD_MSB_REG, buf[1]); + if (rc < 0) { + dev_err(chg->dev, "Couldn't set term MSB rc=%d\n", rc); + return rc; + } + rc = smblib_write(chg, CHGR_ADC_ITERM_UP_THD_LSB_REG, + buf[0]); +#else rc = smblib_batch_write(chg, CHGR_ADC_ITERM_UP_THD_MSB_REG, (u8 *)&raw_hi_thresh, 2); +#endif if (rc < 0) { dev_err(chg->dev, "Couldn't configure ITERM threshold HIGH rc=%d\n", rc); @@ -2591,7 +4004,11 @@ static int smb5_configure_float_charger(struct smb5 *chip) chg->float_cfg = val; /* Update float charger setting and set DCD timeout 300ms */ rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + FLOAT_OPTIONS_MASK, val); +#else FLOAT_OPTIONS_MASK | DCD_TIMEOUT_SEL_BIT, val); +#endif if (rc < 0) { dev_err(chg->dev, "Couldn't change float charger setting rc=%d\n", rc); @@ -2693,6 +4110,11 @@ static int smb5_init_hw(struct smb5 *chip) smblib_get_charge_param(chg, &chg->param.fv, &chg->batt_profile_fv_uv); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->batt_profile_fv_uv = chip->dt.batt_profile_fv_uv; + chg->non_fcc_batt_profile_fv_uv = chip->dt.non_fcc_batt_profile_fv_uv; +#endif + smblib_get_charge_param(chg, &chg->param.usb_icl, &chg->default_icl_ua); smblib_get_charge_param(chg, &chg->param.aicl_5v_threshold, @@ -2774,6 +4196,12 @@ static int smb5_init_hw(struct smb5 *chip) DEFAULT_VOTER, chip->dt.no_battery, 0); vote(chg->dc_suspend_votable, DEFAULT_VOTER, chip->dt.no_battery, 0); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* if use bq wireless solution, should suspend DC_IN path forever */ + if (chg->wireless_bq) + rc = vote(chg->dc_suspend_votable, WIRELESS_BY_USB_IN_VOTER, + true, 0); +#endif vote(chg->fcc_votable, HW_LIMIT_VOTER, chip->dt.batt_profile_fcc_ua > 0, chip->dt.batt_profile_fcc_ua); vote(chg->fv_votable, HW_LIMIT_VOTER, @@ -2784,11 +4212,27 @@ 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); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + vote(chg->fcc_votable, BATT_VERIFY_VOTER, + chip->dt.batt_unverify_fcc_ua > 0, chip->dt.batt_unverify_fcc_ua); + + if (chg->ext_bbc) { + vote(chg->usb_icl_votable, BBC_CHARGER_VOTER, true , 0); + vote(chg->chg_disable_votable, BBC_CHARGER_VOTER, true, 1); + } +#endif /* Some h/w limit maximum supported ICL */ vote(chg->usb_icl_votable, HW_LIMIT_VOTER, chg->hw_max_icl_ua > 0, chg->hw_max_icl_ua); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* if support ffc, default vfloat set non-fcc voltage, only fast charge need override to ffc voltage */ + if (chg->support_ffc) + vote(chg->fv_votable, NON_FFC_VFLOAT_VOTER, + true, chg->non_fcc_batt_profile_fv_uv); +#endif + /* Initialize DC peripheral configurations */ rc = smb5_init_dc_peripheral(chg); if (rc < 0) @@ -2800,6 +4244,11 @@ static int smb5_init_hw(struct smb5 *chip) */ mask = USBIN_AICL_PERIODIC_RERUN_EN_BIT | USBIN_AICL_ADC_EN_BIT | USBIN_AICL_EN_BIT | SUSPEND_ON_COLLAPSE_USBIN_BIT; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->ext_bbc) + val = 0; + else +#endif val = USBIN_AICL_PERIODIC_RERUN_EN_BIT | USBIN_AICL_EN_BIT; if (!chip->dt.disable_suspend_on_collapse) val |= SUSPEND_ON_COLLAPSE_USBIN_BIT; @@ -2935,6 +4384,16 @@ static int smb5_init_hw(struct smb5 *chip) return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + rc = smblib_masked_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, + USBIN_ADAPTER_ALLOW_MASK, USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V); + if (rc < 0) { + dev_err(chg->dev, "Couldn't set USBIN_ADAPTER_ALLOW_CFG_REG rc=%d\n", + rc); + return rc; + } +#endif + if (chg->connector_pull_up != -EINVAL) { rc = smb5_configure_internal_pull(chg, CONN_THERM, get_valid_pullup(chg->connector_pull_up)); @@ -2957,6 +4416,20 @@ static int smb5_init_hw(struct smb5 *chip) } } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* + * 1. set 0x154a bit2 to 1 to fix huawei scp cable 3A for SDP issue + * 2. set 0x154a bit3 to 0 to enable AICL for debug access mode cable + * 3. set 0x154a bit0 to 1 to enable debug access mode detect + * 4. set 0x154a bit4 to 0 to disable typec FMB mode + */ + rc = smblib_masked_write(chg, TYPE_C_DEBUG_ACC_SNK_CFG, 0x1F, 0x07); + if (rc < 0) { + dev_err(chg->dev, "Couldn't configure TYPE_C_DEBUG_ACC_SNK_CFG rc=%d\n", + rc); + return rc; + } +#endif return rc; } @@ -3005,11 +4478,25 @@ static int smb5_determine_initial_status(struct smb5 *chip) } chg->early_usb_attach = val.intval; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + rc = smblib_get_prop_dc_present(chg, &val); + if (rc < 0) { + pr_err("Couldn't get usb present rc=%d\n", rc); + return rc; + } + chg->early_dc_attach = val.intval; +#endif + if (chg->bms_psy) smblib_suspend_on_debug_battery(chg); usb_plugin_irq_handler(0, &irq_data); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->wireless_bq) + dc_power_on_irq_handler(0, &irq_data); +#else dc_plugin_irq_handler(0, &irq_data); +#endif typec_attach_detach_irq_handler(0, &irq_data); typec_state_change_irq_handler(0, &irq_data); usb_source_change_irq_handler(0, &irq_data); @@ -3090,7 +4577,9 @@ static struct smb_irq_info smb5_irqs[] = { [BAT_TEMP_IRQ] = { .name = "bat-temp", .handler = batt_temp_changed_irq_handler, +#ifndef CONFIG_MACH_XIAOMI_SM8250 .wake = true, +#endif }, [ALL_CHNL_CONV_DONE_IRQ] = { .name = "all-chnl-conv-done", @@ -3177,7 +4666,12 @@ static struct smb_irq_info smb5_irqs[] = { }, [DCIN_PON_IRQ] = { .name = "dcin-pon", +#ifdef CONFIG_MACH_XIAOMI_SM8250 + .handler = dc_power_on_irq_handler, + .wake = true, +#else .handler = default_irq_handler, +#endif }, [DCIN_EN_IRQ] = { .name = "dcin-en", @@ -3220,7 +4714,11 @@ static struct smb_irq_info smb5_irqs[] = { [WDOG_SNARL_IRQ] = { .name = "wdog-snarl", .handler = wdog_snarl_irq_handler, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + .wake = false, +#else .wake = true, +#endif }, [WDOG_BARK_IRQ] = { .name = "wdog-bark", @@ -3365,6 +4863,15 @@ static int smb5_request_interrupts(struct smb5 *chip) } } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /*enable batt_temp irq when plugin usb poweron charging*/ + if (chg->irq_info[BAT_TEMP_IRQ].irq + && (chg->early_usb_attach || chg->early_dc_attach)) { + enable_irq_wake(chg->irq_info[BAT_TEMP_IRQ].irq); + chg->batt_temp_irq_enabled = true; + } +#endif + vote(chg->limited_irq_disable_votable, CHARGER_TYPE_VOTER, true, 0); vote(chg->hdc_irq_disable_votable, CHARGER_TYPE_VOTER, true, 0); @@ -3543,6 +5050,14 @@ static int smb5_init_typec_class(struct smb5 *chip) return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +struct usbpd *smb_get_usbpd(void) +{ + return __smbchg->pd; +} +EXPORT_SYMBOL(smb_get_usbpd); +#endif + static int smb5_probe(struct platform_device *pdev) { struct smb5 *chip; @@ -3556,7 +5071,13 @@ static int smb5_probe(struct platform_device *pdev) chg = &chip->chg; chg->dev = &pdev->dev; chg->debug_mask = &__debug_mask; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->thermal_fcc_override = 0; +#endif chg->pd_disabled = 0; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->apdo_max = 0; +#endif chg->weak_chg_icl_ua = 500000; chg->mode = PARALLEL_MASTER; chg->irq_info = smb5_irqs; @@ -3564,6 +5085,10 @@ static int smb5_probe(struct platform_device *pdev) chg->connector_health = -EINVAL; chg->otg_present = false; chg->main_fcc_max = -EINVAL; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->warm_fake_charging = false; + chg->fake_dc_on = false; +#endif mutex_init(&chg->adc_lock); chg->regmap = dev_get_regmap(chg->dev->parent, NULL); @@ -3585,6 +5110,11 @@ static int smb5_probe(struct platform_device *pdev) return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->use_bq_pump) + __smbchg = chg; +#endif + if (alarmtimer_get_rtcdev()) alarm_init(&chg->lpd_recheck_timer, ALARM_REALTIME, smblib_lpd_recheck_timer); @@ -3600,6 +5130,11 @@ static int smb5_probe(struct platform_device *pdev) /* set driver data before resources request it */ platform_set_drvdata(pdev, chip); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* wakeup init should be done at the beginning of smb5_probe */ + device_init_wakeup(chg->dev, true); +#endif + /* extcon registration */ chg->extcon = devm_extcon_dev_allocate(chg->dev, smblib_extcon_cable); if (IS_ERR(chg->extcon)) { @@ -3712,6 +5247,16 @@ static int smb5_probe(struct platform_device *pdev) goto cleanup; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->support_wireless) { + rc = smb5_init_wireless_psy(chip); + if (rc < 0) { + pr_err("Couldn't initialize wireless psy rc=%d\n", rc); + goto cleanup; + } + } +#endif + rc = smb5_request_interrupts(chip); if (rc < 0) { pr_err("Couldn't request interrupts rc=%d\n", rc); @@ -3738,7 +5283,11 @@ static int smb5_probe(struct platform_device *pdev) goto free_irq; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + schedule_delayed_work(&chg->reg_work, 30 * HZ); +#else device_init_wakeup(chg->dev, true); +#endif pr_info("QPNP SMB5 probed successfully\n"); @@ -3786,6 +5335,10 @@ static void smb5_shutdown(struct platform_device *pdev) smblib_masked_write(chg, TYPE_C_MODE_CFG_REG, TYPEC_POWER_ROLE_CMD_MASK, EN_SNK_ONLY_BIT); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /*fix PD bug.Set 0x1360 = 0x0c when shutdown*/ + smblib_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, USBIN_ADAPTER_ALLOW_5V_TO_12V); +#endif /* force enable and rerun APSD */ smblib_apsd_enable(chg, true); smblib_hvdcp_exit_config(chg); diff --git a/drivers/power/supply/qcom/smb5-lib.c b/drivers/power/supply/qcom/smb5-lib.c index 7818896c62c9..d74a3579b1af 100644 --- a/drivers/power/supply/qcom/smb5-lib.c +++ b/drivers/power/supply/qcom/smb5-lib.c @@ -40,8 +40,19 @@ || typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH) \ && (!chg->typec_legacy || chg->typec_legacy_use_rp_icl)) +#ifdef CONFIG_MACH_XIAOMI_SM8250 +bool off_charge_flag; +static bool first_boot_flag; +#endif + static void update_sw_icl_max(struct smb_charger *chg, int pst); static int smblib_get_prop_typec_mode(struct smb_charger *chg); +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static int smblib_dc_therm_charging(struct smb_charger *chg, + int temp_level); +static int smblib_get_batt_voltage_now(struct smb_charger *chg, + union power_supply_propval *val); +#endif int smblib_read(struct smb_charger *chg, u16 addr, u8 *val) { @@ -177,6 +188,13 @@ int smblib_icl_override(struct smb_charger *chg, enum icl_override_mode mode) icl_override = 0; apsd_override = ICL_OVERRIDE_AFTER_APSD_BIT; break; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + case SW_OVERRIDE_NO_CC_MODE: + usb51_mode = USBIN_MODE_CHG_BIT; + icl_override = 1; + apsd_override = ICL_OVERRIDE_AFTER_APSD_BIT; + break; +#endif case HW_AUTO_MODE: default: usb51_mode = USBIN_MODE_CHG_BIT; @@ -481,6 +499,9 @@ enum { FLOAT, HVDCP2, HVDCP3, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + HVDCP3P5, +#endif MAX_TYPES }; @@ -525,6 +546,12 @@ static const struct apsd_result smblib_apsd_results[] = { .bit = DCP_CHARGER_BIT | QC_3P0_BIT, .pst = POWER_SUPPLY_TYPE_USB_HVDCP_3, }, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + [HVDCP3P5] = { + .name = "HVDCP3P5", + .pst = POWER_SUPPLY_TYPE_USB_HVDCP_3P5, + }, +#endif }; static const struct apsd_result *smblib_get_apsd_result(struct smb_charger *chg) @@ -557,10 +584,32 @@ static const struct apsd_result *smblib_get_apsd_result(struct smb_charger *chg) } if (apsd_stat & QC_CHARGER_BIT) { +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->qc3p5_supported) { + if (result == &smblib_apsd_results[HVDCP3] + && chg->qc3p5_authenticated) { + result = &smblib_apsd_results[HVDCP3P5]; + } else if ((result != &smblib_apsd_results[HVDCP3P5]) + && (result != &smblib_apsd_results[HVDCP3])) { + /* If not QC3 or QC3.5, return QC2 */ + result = &smblib_apsd_results[HVDCP2]; + } + } else { +#endif /* since its a qc_charger, either return HVDCP3 or HVDCP2 */ if (result != &smblib_apsd_results[HVDCP3]) result = &smblib_apsd_results[HVDCP2]; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (result == &smblib_apsd_results[HVDCP3] && chg->fake_hvdcp3) { + pr_info("fake hvdcp3, set as hvdcp2"); + result = &smblib_apsd_results[HVDCP2]; + vote(chg->usb_icl_votable, MAIN_ICL_MIN_VOTER, false, 0); + vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER, true, 2000000); + vote(chg->fcc_votable, HVDCP2_FCC_VOTER, true, 2000000); + } + } +#endif return result; } @@ -738,6 +787,203 @@ int smblib_set_usb_suspend(struct smb_charger *chg, bool suspend) return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_get_fastcharge_mode(struct smb_charger *chg) +{ + union power_supply_propval pval = {0,}; + int rc = 0; + + if (!chg->bms_psy) + return 0; + + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_FASTCHARGE_MODE, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't write fastcharge mode:%d\n", rc); + return rc; + } + + return pval.intval; +} + +#ifdef CONFIG_QPNP_FG_GEN4 +static int smb5_config_sys_iterm(struct smb_charger *chg, bool ffc_enable) +{ + union power_supply_propval pval = {0,}; + int rc; + + pval.intval = ffc_enable; + + rc = power_supply_set_property(chg->bms_psy, + POWER_SUPPLY_PROP_SYS_TERMINATION_CURRENT, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't set ffc termination current:%d\n", rc); + return rc; + } + rc = power_supply_set_property(chg->bms_psy, + POWER_SUPPLY_PROP_VBATT_FULL_VOL, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't set ffc vabtt full:%d\n", rc); + return rc; + } + + rc = power_supply_set_property(chg->bms_psy, + POWER_SUPPLY_PROP_KI_COEFF_CURRENT, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't set ffc ki_coefet:%d\n", rc); + return rc; + } + return rc; +} +#endif + +static int smb5_config_iterm(struct smb_charger *chg, int hi_thresh, int low_thresh) +{ + s16 raw_hi_thresh, raw_lo_thresh; + u8 *buf; + int rc = 0; + + rc = smblib_masked_write(chg, CHGR_ADC_TERM_CFG_REG, + TERM_BASED_ON_SYNC_CONV_OR_SAMPLE_CNT, + TERM_BASED_ON_SAMPLE_CNT); + if (rc < 0) { + dev_err(chg->dev, "Couldn't configure ADC_ITERM_CFG rc=%d\n", + rc); + return rc; + } + + /* + * Conversion: + * raw (A) = (scaled_mA * ADC_CHG_TERM_MASK) / (10 * 1000) + * Note: raw needs to be converted to big-endian format. + */ + + if (hi_thresh) { + raw_hi_thresh = ((hi_thresh * ADC_CHG_TERM_MASK) / 10000); + raw_hi_thresh = sign_extend32(raw_hi_thresh, 15); + buf = (u8 *)&raw_hi_thresh; + rc = smblib_write(chg, CHGR_ADC_ITERM_UP_THD_MSB_REG, + buf[1]); + if (rc < 0) { + dev_err(chg->dev, "Couldn't set term MSB rc=%d\n", + rc); + return rc; + } + rc = smblib_write(chg, CHGR_ADC_ITERM_UP_THD_LSB_REG, + buf[0]); + if (rc < 0) { + dev_err(chg->dev, "Couldn't set term LSB rc=%d\n", + rc); + return rc; + } + } + + if (low_thresh) { + raw_lo_thresh = ((low_thresh * ADC_CHG_TERM_MASK) / 10000); + raw_lo_thresh = sign_extend32(raw_lo_thresh, 15); + buf = (u8 *)&raw_lo_thresh; + raw_lo_thresh = buf[1] | (buf[0] << 8); + + rc = smblib_batch_write(chg, CHGR_ADC_ITERM_LO_THD_MSB_REG, + (u8 *)&raw_lo_thresh, 2); + if (rc < 0) { + dev_err(chg->dev, "Couldn't configure ITERM threshold LOW rc=%d\n", + rc); + return rc; + } + } + + return 0; +} + +int smblib_set_fastcharge_mode(struct smb_charger *chg, bool enable) +{ + union power_supply_propval pval = {0,}; + int rc = 0; + int termi = -220, batt_temp; + + if (!chg->bms_psy) + return 0; + +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_AUTHENTIC, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't get battery authentic:%d\n", rc); + return rc; + } + if (!pval.intval) + enable = false; +#endif + + /*if soc > 95 do not set fastcharge flag*/ + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_CAPACITY, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't get bms capacity:%d\n", rc); + return rc; + } + if (enable && pval.intval >= 95) { + smblib_dbg(chg, PR_MISC, "soc:%d is more than 95" + "do not setfastcharge mode\n", pval.intval); + enable = false; + } + + /*if temp > 450 or temp < 100 do not set fastcharge flag*/ + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_TEMP, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't get bms capacity:%d\n", rc); + goto set_term; + } + batt_temp = pval.intval; + if (enable && (pval.intval >= chg->chg_warm_threshold || + pval.intval <= chg->chg_cool_threshold)) { + smblib_dbg(chg, PR_MISC, "temp:%d is abort" + "do not setfastcharge mode\n", pval.intval); + enable = false; + } + + pval.intval = enable; + rc = power_supply_set_property(chg->bms_psy, + POWER_SUPPLY_PROP_FASTCHARGE_MODE, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't write fastcharge mode:%d\n", rc); + goto set_term; + } + + if (enable) { + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_FFC_TERMINATION_CURRENT, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't ffc termination current:%d\n", rc); + goto set_term; + } + termi = pval.intval; + } else { + termi = chg->chg_term_current_thresh_hi_from_dts; + } +#ifdef CONFIG_QPNP_FG_GEN4 + smb5_config_sys_iterm(chg, enable); +#endif +set_term: + smb5_config_iterm(chg, termi, 50); + + if (batt_temp < chg->chg_warm_threshold + && batt_temp > chg->chg_cool_threshold) { + rc = vote(chg->fcc_votable, PD_VERIFED_VOTER, + !enable, PD_UNVERIFED_CURRENT); + } + + if (chg->support_ffc) + rc = vote(chg->fv_votable, NON_FFC_VFLOAT_VOTER, !enable, chg->non_fcc_batt_profile_fv_uv); + + pr_info("fastcharge mode:%d termi:%d\n", enable, termi); + + return 0; +} +#endif + int smblib_set_dc_suspend(struct smb_charger *chg, bool suspend) { int rc = 0; @@ -751,6 +997,165 @@ int smblib_set_dc_suspend(struct smb_charger *chg, bool suspend) return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_set_prop_wireless_wakelock(struct smb_charger *chg, + const union power_supply_propval *val) +{ + if (val->intval) { + /* hold a wakeup source when wireless tx is present */ + vote(chg->awake_votable, DC_AWAKE_VOTER, true, 0); + } else { + /* clear chg_awake wakeup source when wireless tx is absent */ + vote(chg->awake_votable, DC_AWAKE_VOTER, false, 0); + } + return 0; +} + +static void smblib_wl_bq_enable(struct smb_charger *chg, bool enable) +{ + int rc = 0; + + if (enable) { + rc = gpio_direction_output(chg->bq_en_gpio, 1); + if (rc) + pr_err("unable to set bq en to high rc = %d\n", rc); + } else { + rc = gpio_direction_output(chg->bq_en_gpio, 0); + if (rc) + pr_err("unable to set bq en to high rc = %d\n", rc); + } + + rc = gpio_get_value(chg->bq_en_gpio); + smblib_dbg(chg, PR_OEM, "bq_en_gpio: %d\n", rc); +} + +static void smblib_dc_chg_q1_enable(struct smb_charger *chg, bool enable) +{ + int rc = 0; + + smblib_dbg(chg, PR_OEM, "enable: %d\n", enable); + if (enable) { + rc = gpio_direction_output(chg->dc_chg_gpio, 1); + if (rc) + pr_err("unable to set dc_chg_en to high rc = %d\n", rc); + } else { + rc = gpio_direction_output(chg->dc_chg_gpio, 0); + if (rc) + pr_err("unable to set dc_chg_en to high rc = %d\n", rc); + } + + rc = gpio_get_value(chg->dc_chg_gpio); + smblib_dbg(chg, PR_OEM, "dc_chg_gpio: %d\n", rc); +} +static void smblib_set_wireless_otg_state(struct smb_charger *chg, bool enable) +{ + union power_supply_propval val = {0, }; + if (!chg->wls_psy) { + chg->wls_psy = power_supply_get_by_name("wireless"); + if (!chg->wls_psy) + return; + } + val.intval = enable; + if (chg->wls_psy) + power_supply_set_property(chg->wls_psy , POWER_SUPPLY_PROP_OTG_STATE, &val); + +} + +static void smblib_ncp3902_enable(struct smb_charger *chg, bool enable) +{ + int rc = 0; + + printk("smblib_ncp3902_enable: %d\n", enable); + if (!chg->ncp3902_en_gpio) + return; + + if (enable) { + gpio_set_value(chg->ncp3902_en_gpio, 1); + } else { + gpio_set_value(chg->ncp3902_en_gpio, 0); + } + rc = gpio_get_value(chg->ncp3902_en_gpio); + smblib_dbg(chg, PR_OEM, "ncp3902_en_gpio: %d\n", rc); +} +static void ncp3902_pass_enable(struct smb_charger *chg, bool enable) +{ + int rc = 0; + + if (!chg->ncp3902_pass_en_gpio) + return; + + if (enable) { + gpio_set_value(chg->ncp3902_pass_en_gpio, 1); + } else { + gpio_set_value(chg->ncp3902_pass_en_gpio, 0); + } + rc = gpio_get_value(chg->ncp3902_pass_en_gpio); + smblib_dbg(chg, PR_OEM, "ncp3902_pass_en_gpio: %d\n", rc); +} + +static bool is_ncp3902_pull_low(struct smb_charger *chg) +{ + int rc = 0; + + if (!chg->ncp3902_en_gpio) + return false; + + rc = gpio_get_value(chg->ncp3902_en_gpio); + smblib_dbg(chg, PR_OEM, "ncp3902_en_gpio val is now: %d\n", rc); + + if (!rc) + return true; + else + return false; +} + +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 +static void smblib_check_batt_authentic(struct smb_charger *chg) +{ + int rc = 0; + int authen_result = -1; + union power_supply_propval pval = {0,}; + if (chg->batt_verify_psy && chg->bms_psy) { + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_AUTHENTIC, &pval); + if (!rc) + authen_result = pval.intval; + pr_err("authen_result: %d\n", authen_result); + if (!authen_result) { + pval.intval = 1; + rc = power_supply_set_property(chg->batt_verify_psy, + POWER_SUPPLY_PROP_AUTHENTIC, &pval); + if (rc) + pr_err("set batt_verify authentic prop failed: %d\n", + rc); + } + /* notify smblib_notifier_call to reset BATT_VERIFY_VOTER fcc voter */ + power_supply_changed(chg->bms_psy); + } +} +#endif + +int smblib_set_prop_input_current_max(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc = 0; + + if (!chg->wireless_bq) + return rc; + + rc = vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, + false, 0); + vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); + + rc = vote(chg->usb_icl_votable, WIRELESS_BY_USB_IN_VOTER, + true, val->intval); + + smblib_dc_therm_charging(chg, chg->dc_temp_level); + + return rc; +} +#endif + static int smblib_usb_pd_adapter_allowance_override(struct smb_charger *chg, u8 allowed_voltage) { @@ -941,6 +1346,25 @@ int smblib_get_qc3_main_icl_offset(struct smb_charger *chg, int *offset_ua) return 0; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_get_prop_from_bbc(struct smb_charger *chg, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int rc; + + if (!chg->bbc_psy) + chg->bbc_psy = power_supply_get_by_name("bbc"); + + if (!chg->bbc_psy || !chg->ext_bbc) + return -EINVAL; + + rc = power_supply_get_property(chg->bbc_psy, psp, val); + + return rc; +} +#endif + int smblib_get_prop_from_bms(struct smb_charger *chg, enum power_supply_property psp, union power_supply_propval *val) @@ -1073,12 +1497,21 @@ void smblib_rerun_apsd(struct smb_charger *chg) { int rc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->fake_hvdcp3 && + (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP)) { + smblib_dbg(chg, PR_MISC, "fake_hvdcp3: no rerun apsd.\n"); + } else { +#endif smblib_dbg(chg, PR_MISC, "re-running APSD\n"); rc = smblib_masked_write(chg, CMD_APSD_REG, APSD_RERUN_BIT, APSD_RERUN_BIT); if (rc < 0) smblib_err(chg, "Couldn't re-run APSD rc=%d\n", rc); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + } +#endif } static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg) @@ -1088,16 +1521,32 @@ static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg) /* 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; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_PD; +#endif } else if (chg->qc3p5_detected) { chg->real_charger_type = POWER_SUPPLY_TYPE_USB_HVDCP_3P5; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_PD; +#endif } else { /* * Update real charger type only if its not FLOAT * detected as as SDP */ if (!(apsd_result->pst == POWER_SUPPLY_TYPE_USB_FLOAT && +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->real_charger_type == POWER_SUPPLY_TYPE_USB) && + (!chg->qc3p5_supported || chg->qc3p5_auth_complete || + apsd_result->pst != POWER_SUPPLY_TYPE_USB_HVDCP_3)) { +#else chg->real_charger_type == POWER_SUPPLY_TYPE_USB)) +#endif chg->real_charger_type = apsd_result->pst; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->usb_psy_desc.type = apsd_result->pst; + } +#endif } smblib_dbg(chg, PR_MISC, "APSD=%s PD=%d QC3P5=%d\n", @@ -1110,14 +1559,40 @@ static int smblib_notifier_call(struct notifier_block *nb, { struct power_supply *psy = v; struct smb_charger *chg = container_of(nb, struct smb_charger, nb); +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + int rc; + union power_supply_propval pval = {0,}; +#endif if (!strcmp(psy->desc->name, "bms")) { if (!chg->bms_psy) chg->bms_psy = psy; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (ev == PSY_EVENT_PROP_CHANGED) { +#else if (ev == PSY_EVENT_PROP_CHANGED) +#endif +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_AUTHENTIC, &pval); + if (rc < 0) { + pr_err("Couldn't get batt verify status rc=%d\n", rc); + } + chg->batt_verified = pval.intval; + schedule_work(&chg->batt_verify_update_work); +#endif schedule_work(&chg->bms_update_work); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + } +#endif } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (!strcmp(psy->desc->name, "battery")) { + schedule_work(&chg->batt_update_work); + } +#endif + if (chg->jeita_configured == JEITA_CFG_NONE) schedule_work(&chg->jeita_update_work); @@ -1346,6 +1821,9 @@ static int smblib_get_pulse_cnt(struct smb_charger *chg, int *count) } #define USBIN_25MA 25000 +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define USBIN_30MA 30000 +#endif #define USBIN_100MA 100000 #define USBIN_150MA 150000 #define USBIN_500MA 500000 @@ -1410,10 +1888,19 @@ static int set_sdp_current(struct smb_charger *chg, int icl_ua) return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define CLEAN_CP_TO_SW_DELAY_MS 500 +#endif int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) { int rc = 0; enum icl_override_mode icl_override = HW_AUTO_MODE; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + union power_supply_propval pval = {0,}; + int dc_power_on = 0; + static int pre_icl; +#endif + /* suspend if 25mA or less is requested */ bool suspend = (icl_ua <= USBIN_25MA); @@ -1426,12 +1913,44 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY) return 0; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (pre_icl == 0 && icl_ua == 3000000) { + chg->cp_to_sw_status = true; + schedule_delayed_work(&chg->clean_cp_to_sw_work, + msecs_to_jiffies(CLEAN_CP_TO_SW_DELAY_MS)); + } + pre_icl = icl_ua; +#endif + if (suspend) return smblib_set_usb_suspend(chg, true); if (icl_ua == INT_MAX) goto set_mode; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->wireless_bq) { + smblib_get_prop_dc_present(chg, &pval); + dc_power_on = pval.intval; + } + if (chg->wireless_bq && (dc_power_on == 1)) { + if ((chg->typec_mode >= POWER_SUPPLY_TYPEC_NONE) + && (chg->real_charger_type >= POWER_SUPPLY_TYPE_USB + || chg->real_charger_type == POWER_SUPPLY_TYPE_UNKNOWN)) { + rc = smblib_set_charge_param(chg, &chg->param.usb_icl, icl_ua); + icl_override = SW_OVERRIDE_NO_CC_MODE; + goto set_mode; + } + } + + if ((chg->typec_mode == POWER_SUPPLY_TYPEC_NONE) + && (chg->real_charger_type >= POWER_SUPPLY_TYPE_USB)) { + rc = smblib_set_charge_param(chg, &chg->param.usb_icl, icl_ua); + icl_override = SW_OVERRIDE_NO_CC_MODE; + goto set_mode; + } +#endif + /* configure current */ if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB && (chg->typec_legacy @@ -1448,7 +1967,11 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua) * current limit is 500mA or below for better accuracy; in case * of error, proceed to use USB high-current mode. */ +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (icl_ua < USBIN_100MA) { +#else if (icl_ua <= USBIN_500MA) { +#endif rc = set_sdp_current(chg, icl_ua); if (rc >= 0) goto unsuspend; @@ -1913,10 +2436,24 @@ int smblib_get_prop_input_suspend(struct smb_charger *chg, { val->intval = (get_client_vote(chg->usb_icl_votable, USER_VOTER) == 0) +#ifdef CONFIG_MACH_XIAOMI_SM8250 + || get_client_vote(chg->dc_suspend_votable, USER_VOTER); +#else && get_client_vote(chg->dc_suspend_votable, USER_VOTER); +#endif return 0; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_get_prop_battery_input_suspend(struct smb_charger *chg, + union power_supply_propval *val) +{ + val->intval + = (get_client_vote(chg->fcc_votable, USER_VOTER) == 0); + return 0; +} +#endif + int smblib_get_prop_batt_present(struct smb_charger *chg, union power_supply_propval *val) { @@ -1935,21 +2472,123 @@ int smblib_get_prop_batt_present(struct smb_charger *chg, return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static bool smblib_check_vbat_before_shutdown(struct smb_charger *chg) +{ + int rc; + int vbat_uv = 0; + union power_supply_propval pval = {0,}; + + rc = smblib_get_batt_voltage_now(chg, &pval); + if (rc < 0) { + pr_err("Couldn't get vbat rc=%d\n", rc); + return false; + } + vbat_uv = pval.intval; + + /* + * if battery soc is 0% but vbat is above 3450mV + * report 1% to improve user experience + */ + if (vbat_uv > (CUTOFF_VOL_THR + CUTOFF_VOL_HYS) + && !chg->vbat_critical_low_triggered) { + return true; + } else if (!chg->vbat_critical_low_triggered) { + chg->vbat_critical_low_triggered = true; + return false; + } else { + return false; + } +} + +static void smblib_check_input_status(struct smb_charger *chg) +{ + int rc; + int input_present = 0, vbat_uv = 0; + union power_supply_propval pval = {0,}; + + rc = smblib_is_input_present(chg, &input_present); + if (rc < 0) + return; + + if (input_present == INPUT_NOT_PRESENT) + return; + + rc = smblib_get_batt_voltage_now(chg, &pval); + if (rc < 0) { + pr_err("Couldn't get vbat rc=%d\n", rc); + return; + } + vbat_uv = pval.intval; + pr_err("vbat_uv: %d\n", vbat_uv); + + /* + * if battery soc is 0%, vbat is below 3400mV and input is present in + * normal mode (not power-off charging mode), set usb/usb_port/dc + * online to false to notify system to power off. + */ + if ((input_present & INPUT_PRESENT_DC + || input_present & INPUT_PRESENT_USB) + && !off_charge_flag + && (vbat_uv <= CUTOFF_VOL_THR)) { + chg->report_input_absent = true; + power_supply_changed(chg->batt_psy); + } +} +#endif + int smblib_get_prop_batt_capacity(struct smb_charger *chg, union power_supply_propval *val) { int rc = -EINVAL; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->fake_capacity >= 0 && chg->fake_capacity <= 100) { +#else if (chg->fake_capacity >= 0) { +#endif val->intval = chg->fake_capacity; return 0; } rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_CAPACITY, val); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (val->intval == 0) { + smblib_check_input_status(chg); + if (!chg->report_input_absent + && smblib_check_vbat_before_shutdown(chg)) + val->intval = 1; + } +#endif + return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_get_prop_batt_capacity_level(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc,cap; + union power_supply_propval capacity; + + rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_CAPACITY, &capacity); + + cap=capacity.intval; + if (cap == 0) + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + if (cap > 0 && cap <= 20) + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW; + if (cap > 20 && cap <= 80) + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + if (cap > 80 && cap <= 99) + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_HIGH; + if (cap == 100) + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL; + return rc; +} +#endif + static bool is_charging_paused(struct smb_charger *chg) { int rc; @@ -1964,14 +2603,50 @@ static bool is_charging_paused(struct smb_charger *chg) return val & CHARGING_PAUSE_CMD_BIT; } +#ifndef CONFIG_MACH_XIAOMI_SM8250 #define CUTOFF_COUNT 3 +#endif + +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_get_prop_battery_charging_enabled(struct smb_charger *chg, + union power_supply_propval *val) +{ + int icl = 0; + if (chg->is_qc_class_a && !chg->qc3_raise_done) + icl = MAIN_ICL_MIN; + + if (chg->six_pin_step_charge_enable) { + val->intval = !(get_client_vote(chg->usb_icl_votable, MAIN_ICL_MIN_VOTER) + == MAIN_ICL_MIN); + } + else { + if (chg->power_good_en) + val->intval = !(get_client_vote(chg->usb_icl_votable, MAIN_CHG_SUSPEND_VOTER) + == MAIN_CHG_SUSPEND_ICL); + else + val->intval = !(get_client_vote(chg->usb_icl_votable, MAIN_CHG_SUSPEND_VOTER) + == icl); + } + return 0; +} +#endif + int smblib_get_prop_batt_status(struct smb_charger *chg, union power_supply_propval *val) { union power_supply_propval pval = {0, }; bool usb_online, dc_online; u8 stat; +#ifndef CONFIG_MACH_XIAOMI_SM8250 int rc, suspend = 0, input_present = 0; +#else + int rc, suspend = 0; + int batt_health_status; + int chg_en; + int icl = 0; + if (chg->is_qc_class_a && !chg->qc3_raise_done) + icl = MAIN_ICL_MIN; +#endif if (chg->fake_chg_status_on_debug_batt) { rc = smblib_get_prop_from_bms(chg, @@ -1985,6 +2660,58 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, } } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->report_input_absent) { + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + return 0; + } + if (chg->fake_plug_out == true) { + val->intval = POWER_SUPPLY_STATUS_CHARGING; + return 0; + } + if (chg->ext_bbc) { + rc = smblib_get_prop_from_bbc(chg, + POWER_SUPPLY_PROP_STATUS, &pval); + if (rc < 0) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + val->intval = pval.intval; + return 0; + } + + if (chg->use_bq_pump && !chg->six_pin_step_charge_enable + && (get_client_vote_locked(chg->usb_icl_votable, + MAIN_CHG_SUSPEND_VOTER) == icl)) { + val->intval = POWER_SUPPLY_STATUS_CHARGING; + return 0; + } + + if (chg->use_bq_pump && chg->six_pin_step_charge_enable + && (get_client_vote_locked(chg->usb_icl_votable, + MAIN_ICL_MIN_VOTER) == MAIN_ICL_MIN)) { + val->intval = POWER_SUPPLY_STATUS_CHARGING; + return 0; + } + + if (chg->use_bq_pump && !chg->six_pin_step_charge_enable + && is_client_vote_enabled_locked(chg->chg_disable_votable, + AFTER_FFC_VOTER)) { + val->intval = POWER_SUPPLY_STATUS_CHARGING; + return 0; + } + + if (chg->fake_chg_status_on_debug_batt) { + rc = smblib_get_prop_from_bms(chg, + POWER_SUPPLY_PROP_DEBUG_BATTERY, &pval); + if (rc < 0) { + pr_err_ratelimited("Couldn't get debug battery prop rc=%d\n", + rc); + } else if (pval.intval == 1) { + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + return 0; + } + } +#endif + rc = smblib_get_prop_batt_health(chg, &pval); if (rc < 0) { smblib_err(chg, "Couldn't get batt health rc=%d\n", rc); @@ -1996,6 +2723,7 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, * supply state as NOT_CHARGING when the battery health reports * over voltage. */ +#ifndef CONFIG_MACH_XIAOMI_SM8250 if (pval.intval == POWER_SUPPLY_HEALTH_OVERVOLTAGE) { val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; return 0; @@ -2022,6 +2750,15 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, } else { chg->cutoff_count = 0; } +#else + if (get_client_vote_locked(chg->usb_icl_votable, JEITA_VOTER) == 0) + /* show charging when JEITA_VOTER 0mA is vote to improve user experience */ + if(pval.intval != POWER_SUPPLY_HEALTH_OVERHEAT + && pval.intval != POWER_SUPPLY_HEALTH_COLD) { + val->intval = POWER_SUPPLY_STATUS_CHARGING; + return 0; + } +#endif if (chg->dbc_usbov) { rc = smblib_get_prop_usb_present(chg, &pval); @@ -2064,6 +2801,30 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, } dc_online = (bool)pval.intval; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (!usb_online && !dc_online) { + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + return rc; + } + + rc = smblib_get_prop_batt_health(chg, &pval); + if (rc < 0) + smblib_err(chg, "Couldn't get batt health rc=%d\n", rc); + + batt_health_status = pval.intval; + if (get_client_vote_locked(chg->usb_icl_votable, JEITA_VOTER) == 0) { + /* show charging when JEITA_VOTER 0mA is vote to improve user experience */ + if (pval.intval != POWER_SUPPLY_HEALTH_OVERHEAT + && pval.intval != POWER_SUPPLY_HEALTH_COLD) { + val->intval = POWER_SUPPLY_STATUS_CHARGING; + return 0; + } + } + + chg_en = !(get_client_vote_locked(chg->usb_icl_votable, MAIN_CHG_SUSPEND_VOTER) + == MAIN_CHG_SUSPEND_ICL); +#endif + 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", @@ -2072,6 +2833,7 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, } stat = stat & BATTERY_CHARGER_STATUS_MASK; +#ifndef CONFIG_MACH_XIAOMI_SM8250 if (!usb_online && !dc_online) { switch (stat) { case TERMINATE_CHARGE: @@ -2084,6 +2846,7 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, } return rc; } +#endif switch (stat) { case TRICKLE_CHARGE: @@ -2094,10 +2857,28 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, break; case TERMINATE_CHARGE: case INHIBIT_CHARGE: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (batt_health_status == POWER_SUPPLY_HEALTH_WARM || chg->capacity <= 98) { + val->intval = POWER_SUPPLY_STATUS_CHARGING; + } else if (!chg_en && chg->power_good_en) { + smblib_dbg(chg, PR_OEM, "report charging when open 25970\n"); + val->intval = POWER_SUPPLY_STATUS_CHARGING; + } else +#endif val->intval = POWER_SUPPLY_STATUS_FULL; break; case DISABLE_CHARGE: case PAUSE_CHARGE: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* + * As from jeita status change, there is very short time not charging, + * to improve user experience, we report charging at this moment. + */ + if (batt_health_status == POWER_SUPPLY_HEALTH_WARM || + chg->cp_to_sw_status == true) { + val->intval = POWER_SUPPLY_STATUS_CHARGING; + } else +#endif val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; break; default: @@ -2120,6 +2901,18 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, return 0; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->wireless_bq && chg->fake_dc_on) { + if ((POWER_SUPPLY_HEALTH_COLD == pval.intval) + || (POWER_SUPPLY_HEALTH_OVERHEAT == pval.intval)) + chg->fake_dc_on = 0; + else if (val->intval != POWER_SUPPLY_STATUS_FULL) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + smblib_dbg(chg, PR_WLS, "fake dc on, battery staus: %d\n", val->intval); + return 0; + } +#endif + if (val->intval != POWER_SUPPLY_STATUS_CHARGING) return 0; @@ -2129,6 +2922,7 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, return 0; } +#ifndef CONFIG_MACH_XIAOMI_SM8250 rc = smblib_read(chg, BATTERY_CHARGER_STATUS_5_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n", @@ -2141,6 +2935,7 @@ int smblib_get_prop_batt_status(struct smb_charger *chg, if (!stat) val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; +#endif return 0; } @@ -2150,6 +2945,11 @@ int smblib_get_prop_batt_charge_type(struct smb_charger *chg, { int rc; u8 stat; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int icl = 0; + if (chg->is_qc_class_a && !chg->qc3_raise_done) + icl = MAIN_ICL_MIN; +#endif rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat); if (rc < 0) { @@ -2158,6 +2958,15 @@ int smblib_get_prop_batt_charge_type(struct smb_charger *chg, return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->use_bq_pump && !chg->six_pin_step_charge_enable + && (get_client_vote_locked(chg->usb_icl_votable, + MAIN_CHG_SUSPEND_VOTER) == icl)) { + val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; + return rc; + } +#endif + switch (stat & BATTERY_CHARGER_STATUS_MASK) { case TRICKLE_CHARGE: case PRE_CHARGE: @@ -2183,6 +2992,9 @@ int smblib_get_prop_batt_health(struct smb_charger *chg, int rc; int effective_fv_uv; u8 stat; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int over_voltage_thr_uv; +#endif rc = smblib_read(chg, BATTERY_CHARGER_STATUS_2_REG, &stat); if (rc < 0) { @@ -2203,7 +3015,19 @@ int smblib_get_prop_batt_health(struct smb_charger *chg, */ effective_fv_uv = get_effective_result_locked( chg->fv_votable); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->six_pin_step_charge_enable) { + if (effective_fv_uv == WARM_VFLOAT_UV) + over_voltage_thr_uv = effective_fv_uv + 100000; + else + over_voltage_thr_uv = chg->batt_profile_fv_uv + 150000; + } else { + over_voltage_thr_uv = effective_fv_uv + 40000; + } + if (pval.intval >= over_voltage_thr_uv) { +#else if (pval.intval >= effective_fv_uv + 40000) { +#endif val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; smblib_err(chg, "battery over-voltage vbat_fg = %duV, fv = %duV\n", pval.intval, effective_fv_uv); @@ -2212,6 +3036,26 @@ int smblib_get_prop_batt_health(struct smb_charger *chg, } } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->ext_fg || chg->chg_warm_threshold == 480) { + //do not use default batt temp threshold + rc = smblib_get_prop_from_bms(chg, + POWER_SUPPLY_PROP_TEMP, &pval); + if (rc < 0) + val->intval = POWER_SUPPLY_HEALTH_GOOD; + + if (pval.intval < BAT_TEMP_COLD) + val->intval = POWER_SUPPLY_HEALTH_COLD; + else if (pval.intval < chg->chg_cool_threshold) + val->intval = POWER_SUPPLY_HEALTH_COOL; + else if (pval.intval > BAT_TEMP_TOO_HOT) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + else if (pval.intval > chg->chg_warm_threshold) + val->intval = POWER_SUPPLY_HEALTH_WARM; + else + val->intval = POWER_SUPPLY_HEALTH_GOOD; + }else { +#endif rc = smblib_read(chg, BATTERY_CHARGER_STATUS_7_REG, &stat); if (rc < 0) { smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n", @@ -2228,11 +3072,23 @@ int smblib_get_prop_batt_health(struct smb_charger *chg, val->intval = POWER_SUPPLY_HEALTH_WARM; else val->intval = POWER_SUPPLY_HEALTH_GOOD; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + } +#endif done: return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_get_prop_dc_temp_level(struct smb_charger *chg, + union power_supply_propval *val) +{ + val->intval = chg->dc_temp_level; + return 0; +} +#endif + int smblib_get_prop_system_temp_level(struct smb_charger *chg, union power_supply_propval *val) { @@ -2313,11 +3169,34 @@ int smblib_get_prop_batt_iterm(struct smb_charger *chg, return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static int smblib_set_wdog_bark_timer(struct smb_charger *chg, + int wdog_timer) +{ + u8 val = 0; + int rc; + + val = (ilog2(wdog_timer / 16) << BARK_WDOG_TIMEOUT_SHIFT) + & BARK_WDOG_TIMEOUT_MASK; + rc = smblib_masked_write(chg, SNARL_BARK_BITE_WD_CFG_REG, + BARK_WDOG_TIMEOUT_MASK, val); + if (rc < 0) { + pr_err("Couldn't configue WD config rc=%d\n", rc); + return rc; + } + return rc; +} +#endif + int smblib_get_prop_batt_charge_done(struct smb_charger *chg, union power_supply_propval *val) { int rc; u8 stat; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + union power_supply_propval pval = {0, }; + int chg_en; +#endif rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat); if (rc < 0) { @@ -2328,9 +3207,74 @@ int smblib_get_prop_batt_charge_done(struct smb_charger *chg, stat = stat & BATTERY_CHARGER_STATUS_MASK; val->intval = (stat == TERMINATE_CHARGE); + +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg_en = !(get_client_vote_locked(chg->usb_icl_votable, MAIN_CHG_SUSPEND_VOTER) + == MAIN_CHG_SUSPEND_ICL); + if (!chg_en && chg->power_good_en && val->intval) { + smblib_dbg(chg, PR_OEM, "force recharge when charge done error\n"); + val->intval = 0; + pval.intval = true; + rc = power_supply_set_property(chg->batt_psy, + POWER_SUPPLY_PROP_FORCE_RECHARGE, &pval); + if (rc < 0) { + smblib_dbg(chg, PR_OEM, "could not set force recharging!\n"); + return rc; + } + return rc; + } + /* if charge is done, clear CHG_AWAKE_VOTER */ + if (val->intval == 1) { + rc = smblib_get_prop_batt_capacity(chg, &pval); + if (rc < 0) + smblib_err(chg, "Couldn't get batt capacity rc=%d\n", rc); + if (pval.intval >= 98) { + /* when charge done, set bark timer to 128s to decrease wakeups */ + smblib_set_wdog_bark_timer(chg, BARK_TIMER_LONG); + vote(chg->awake_votable, CHG_AWAKE_VOTER, false, 0); + } + + if (chg->power_good_en) { + if ((smblib_get_fastcharge_mode(chg) == true) + && (pval.intval >= 98)) + smblib_set_fastcharge_mode(chg, false); + return 0; + } + + if (smblib_get_fastcharge_mode(chg) == true) + smblib_set_fastcharge_mode(chg, false); + } +#endif + return 0; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static int smblib_get_batt_voltage_now(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + rc = smblib_get_prop_from_bms(chg, + POWER_SUPPLY_PROP_VOLTAGE_NOW, val); + + return rc; +} + +static void smblib_get_start_vbat_before_step_charge(struct smb_charger *chg) +{ + int rc; + union power_supply_propval pval = {0, }; + + rc = smblib_get_batt_voltage_now(chg, &pval); + if (!rc) + chg->start_step_vbat = pval.intval; + else + pr_err("could not get vbat vol from bms\n"); + + return; +} +#endif + int smblib_get_batt_current_now(struct smb_charger *chg, union power_supply_propval *val) { @@ -2338,8 +3282,10 @@ int smblib_get_batt_current_now(struct smb_charger *chg, rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_CURRENT_NOW, val); +#ifndef CONFIG_MACH_XIAOMI_SM8250 if (!rc) val->intval *= (-1); +#endif return rc; } @@ -2372,12 +3318,43 @@ int smblib_set_prop_input_suspend(struct smb_charger *chg, return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_set_prop_battery_input_suspend(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc; + + /* vote 0mA when battery suspended */ + rc = vote(chg->fcc_votable, USER_VOTER, (bool)val->intval, 0); + if (rc < 0) { + smblib_err(chg, "Couldn't vote to %s fcc rc=%d\n", + (bool)val->intval ? "suspend" : "resume", rc); + return rc; + } + + power_supply_changed(chg->batt_psy); + return rc; +} +#endif + int smblib_set_prop_batt_capacity(struct smb_charger *chg, const union power_supply_propval *val) { +#ifdef CONFIG_MACH_XIAOMI_SM8250 + union power_supply_propval shutdown_delay_en = {0, }; + /* only enable write reasonable soc value */ + if (val->intval >= 0 && val->intval <= 100) { +#endif chg->fake_capacity = val->intval; power_supply_changed(chg->batt_psy); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + } + + power_supply_set_property(chg->bms_psy, + POWER_SUPPLY_PROP_SHUTDOWN_DELAY_ENABLE, + &shutdown_delay_en); +#endif return 0; } @@ -2396,9 +3373,372 @@ int smblib_set_prop_batt_status(struct smb_charger *chg, return 0; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static void smblib_wireless_set_rx_sleep_pin(struct smb_charger *chg, int enable) +{ + int rc = 0; + union power_supply_propval val = {0, }; + + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) { + chg->wls_chip_psy = chg->idtp_psy; + } else { + chg->wip_psy = power_supply_get_by_name("rx1619"); + if (chg->wip_psy) + chg->wls_chip_psy = chg->wip_psy; + else + return; + } + if (chg->wls_chip_psy) { + printk("smblib_wireless_set_rx_sleep enable: %d\n", enable); + val.intval = enable; + rc = power_supply_set_property(chg->wls_chip_psy, + POWER_SUPPLY_PROP_PIN_ENABLED, &val); + if (rc < 0) { + smblib_err(chg, "Could not set charger control limit =%d\n", rc); + return; + } + } +} + + +static void smblib_set_wireless_present(struct smb_charger *chg, bool present) +{ + union power_supply_propval pval = {0, }; + + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) { + chg->wls_chip_psy = chg->idtp_psy; + } else { + chg->wip_psy = power_supply_get_by_name("rx1619"); + if (chg->wip_psy) + chg->wls_chip_psy = chg->wip_psy; + else + return; + } + + if (chg->wls_chip_psy) { + pval.intval = (int)present; + power_supply_set_property(chg->wls_chip_psy, + POWER_SUPPLY_PROP_PRESENT, &pval); + } +} + +int smblib_get_prop_wireless_version(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) { + chg->wls_chip_psy = chg->idtp_psy; + } else { + chg->wip_psy = power_supply_get_by_name("rx1619"); + if (chg->wip_psy) + chg->wls_chip_psy = chg->wip_psy; + else + return -EINVAL; + } + + if (chg->wls_chip_psy) + rc = power_supply_get_property(chg->wls_chip_psy, + POWER_SUPPLY_PROP_WIRELESS_VERSION, val); + return rc; +} + +int smblib_get_prop_wireless_fw_version(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc; + + chg->idtp_psy = power_supply_get_by_name("idt"); + if (chg->idtp_psy) { + chg->wls_chip_psy = chg->idtp_psy; + } else { + chg->wip_psy = power_supply_get_by_name("rx1619"); + if (chg->wip_psy) + chg->wls_chip_psy = chg->wip_psy; + else + return -EINVAL; + } + + if (chg->wls_chip_psy) + rc = power_supply_get_property(chg->wls_chip_psy, + POWER_SUPPLY_PROP_WIRELESS_FW_VERSION, val); + return rc; +} + +static int smblib_dc_therm_charging(struct smb_charger *chg, + int temp_level) +{ + int thermal_icl_ua = 0; + int thermal_fcc_ua = 0; + int rc = 0; + int is_epp = 0; + union power_supply_propval pval = {0, }; + union power_supply_propval val = {0, }; + + if (!chg->wireless_bq) { + /* + * if use non wireless on bq and usbin, return driectly + * and to do later if use usb mid and dcin wireless charge + */ + return rc; + } + + if (!chg->wls_psy) { + chg->wls_psy = power_supply_get_by_name("wireless"); + if (!chg->wls_psy) + return -ENODEV; + } + rc = power_supply_get_property(chg->wls_psy, + POWER_SUPPLY_PROP_TX_ADAPTER, + &pval); + + rc = power_supply_get_property(chg->wls_psy, + POWER_SUPPLY_PROP_WIRELESS_VERSION, + &val); + + is_epp = val.intval; + + switch (pval.intval) { + case ADAPTER_XIAOMI_QC3: + case ADAPTER_ZIMI_CAR_POWER: + case ADAPTER_XIAOMI_PD: + thermal_fcc_ua = chg->thermal_mitigation_dc_20W[temp_level]; + thermal_icl_ua = chg->thermal_mitigation_dc_20W[temp_level]; + break; + + case ADAPTER_XIAOMI_PD_40W: + case ADAPTER_XIAOMI_PD_50W: + case ADAPTER_XIAOMI_PD_60W: + thermal_fcc_ua = chg->thermal_mitigation_dc[temp_level]; + thermal_icl_ua = chg->thermal_mitigation_dc[temp_level]; + break; + + case ADAPTER_VOICE_BOX: + thermal_fcc_ua = chg->thermal_mitigation_voice[temp_level]; + thermal_icl_ua = chg->thermal_mitigation_voice[temp_level]; + break; + + case ADAPTER_QC2: + thermal_icl_ua = chg->thermal_mitigation_bpp_qc2[temp_level]; + break; + + case ADAPTER_QC3: + if (is_epp == 1)/*is epp*/ + thermal_icl_ua = chg->thermal_mitigation_epp[temp_level]; + else + thermal_icl_ua = chg->thermal_mitigation_bpp_qc3[temp_level]; + break; + + case ADAPTER_PD: + if (is_epp == 1)/*is epp*/ + thermal_icl_ua = chg->thermal_mitigation_epp[temp_level]; + else + thermal_icl_ua = chg->thermal_mitigation_bpp[temp_level]; + break; + + case ADAPTER_AUTH_FAILED: + if (is_epp == 1)/*is epp*/ + thermal_icl_ua = chg->thermal_mitigation_epp[temp_level]; + else + thermal_icl_ua = chg->thermal_mitigation_bpp[temp_level]; + break; + + case ADAPTER_CDP: + case ADAPTER_DCP: + thermal_icl_ua = chg->thermal_mitigation_bpp[temp_level]; + break; + + default: + thermal_icl_ua = chg->thermal_mitigation_bpp[temp_level]; + break; + } + + if (temp_level == 0) { + /* if therm_lvl_sel is 0, clear thermal voter */ + rc = vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, false, 0); + rc = vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, false, 0); + } else { + if (thermal_icl_ua > 0) + rc = vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, + true, thermal_icl_ua); + + if (thermal_fcc_ua > 0) + rc = vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, + true, thermal_fcc_ua); + } + + return rc; +} + +int smblib_set_prop_dc_temp_level(struct smb_charger *chg, + const union power_supply_propval *val) +{ + union power_supply_propval dc_present; + union power_supply_propval batt_temp; + int rc; + + rc = smblib_get_prop_dc_present(chg, &dc_present); + if (rc < 0) { + pr_err("Couldn't get dc present rc=%d\n", rc); + return -EINVAL; + } + + rc = smblib_get_prop_from_bms(chg, + POWER_SUPPLY_PROP_TEMP, &batt_temp); + if (rc < 0) { + pr_err("Couldn't get batt temp rc=%d\n", rc); + return -EINVAL; + } + + if (val->intval < 0) + return -EINVAL; + if (chg->dc_thermal_levels <= 0) + return -EINVAL; + if (val->intval > chg->dc_thermal_levels) + return -EINVAL; + chg->dc_temp_level = val->intval; + + if (chg->dc_temp_level >= (chg->dc_thermal_levels - 1)) + return vote(chg->chg_disable_votable, + THERMAL_DAEMON_VOTER, true, 0); + + vote(chg->chg_disable_votable, THERMAL_DAEMON_VOTER, false, 0); + + if (chg->power_good_en) + smblib_dc_therm_charging(chg, val->intval); + + smblib_dbg(chg, PR_OEM, + "thermal level:%d, batt temp:%d, thermal_levels:%d dc_present=%d\n", + val->intval, batt_temp.intval, chg->dc_thermal_levels,dc_present.intval); + + return 0; +} + +static int smblib_therm_charging(struct smb_charger *chg) +{ + int thermal_icl_ua = 0; + int thermal_fcc_ua = 0; + int rc; + + if (chg->system_temp_level >= MAX_TEMP_LEVEL) + return 0; + + switch (chg->real_charger_type) { + case POWER_SUPPLY_TYPE_USB_HVDCP: + thermal_icl_ua = chg->thermal_mitigation_qc2[chg->system_temp_level]; + break; + case POWER_SUPPLY_TYPE_USB_HVDCP_3: + if (get_client_vote_locked(chg->usb_icl_votable, THERMAL_DAEMON_VOTER) != 0) { + rc = vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, false, 0); + if (rc < 0) + pr_err("Couldn't disable USB thermal ICL vote rc=%d\n", rc); + } + + if (chg->use_bq_pump) { + if (chg->is_qc_class_a) + thermal_fcc_ua = + chg->thermal_fcc_qc3_cp[chg->system_temp_level]; + else + thermal_fcc_ua = + chg->thermal_fcc_qc3_classb_cp[chg->system_temp_level]; + } else { + thermal_fcc_ua = + chg->thermal_fcc_qc3_normal[chg->system_temp_level]; + } + thermal_icl_ua = thermal_fcc_ua; + break; + case POWER_SUPPLY_TYPE_USB_HVDCP_3P5: + thermal_fcc_ua = + chg->thermal_fcc_qc3_cp[chg->system_temp_level]; + break; + case POWER_SUPPLY_TYPE_USB_PD: + if (chg->pd_active == POWER_SUPPLY_PD_PPS_ACTIVE) { + if (chg->pps_thermal_level < 0) + chg->pps_thermal_level = chg->system_temp_level; + thermal_fcc_ua = + chg->thermal_fcc_pps_cp[chg->pps_thermal_level]; + } else { + thermal_icl_ua = + chg->thermal_mitigation_icl[chg->system_temp_level]; + thermal_fcc_ua = + chg->thermal_mitigation_pd_base[chg->system_temp_level]; + } + break; + case POWER_SUPPLY_TYPE_USB_DCP: + default: + thermal_icl_ua = chg->thermal_mitigation_dcp[chg->system_temp_level]; + thermal_fcc_ua = chg->thermal_mitigation[chg->system_temp_level]; + break; + } + + pr_info("###thermal_icl_ua is %d, chg->system_temp_level: %d, thermal_fcc_ua is %d, charger type = %d\n", + thermal_icl_ua, chg->system_temp_level, thermal_fcc_ua, chg->real_charger_type); + if (chg->system_temp_level == 0) { + /* if therm_lvl_sel is 0, clear thermal voter */ + rc = vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, false, 0); + if (rc < 0) + pr_err("Couldn't disable USB thermal ICL vote rc=%d\n", + rc); + rc = vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, false, 0); + if (rc < 0) + pr_err("Couldn't disable USB thermal ICL vote rc=%d\n", + rc); + } else { + pr_info("thermal_icl_ua is %d, chg->system_temp_level: %d, thermal_fcc_ua is %d, charger type = %d\n", + thermal_icl_ua, chg->system_temp_level, thermal_fcc_ua, chg->real_charger_type); + if (thermal_icl_ua > 0) { + rc = vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, true, + thermal_icl_ua); + if (rc < 0) + pr_err("Couldn't enable USB thermal ICL vote rc=%d\n", + rc); + } + if (thermal_fcc_ua > 0) { + rc = vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, true, + thermal_fcc_ua); + if (rc < 0) + pr_err("Couldn't enable USB thermal ICL vote rc=%d\n", + rc); + } + } + return rc; +} + +static void smblib_thermal_setting_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + thermal_setting_work.work); + + if (chg->pps_thermal_level > chg->system_temp_level) { + if (chg->pps_thermal_level - chg->system_temp_level >= 2) + chg->pps_thermal_level -= 2; + else + chg->pps_thermal_level -= 1; + } else if (chg->pps_thermal_level < chg->system_temp_level) { + if (chg->system_temp_level - chg->pps_thermal_level >= 2) + chg->pps_thermal_level += 2; + else + chg->pps_thermal_level += 1; + } + + smblib_therm_charging(chg); + + if (chg->pps_thermal_level != chg->system_temp_level) + schedule_delayed_work(&chg->thermal_setting_work, 3 * HZ); +} +#endif + int smblib_set_prop_system_temp_level(struct smb_charger *chg, const union power_supply_propval *val) { +#ifdef CONFIG_MACH_XIAOMI_SM8250 + union power_supply_propval pval = {0, }; + int rc = 0; +#endif + if (val->intval < 0) return -EINVAL; @@ -2410,19 +3750,371 @@ int smblib_set_prop_system_temp_level(struct smb_charger *chg, chg->system_temp_level = val->intval; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* Check whether USB is online or not */ + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_ONLINE, &pval); + if (rc < 0) + pr_err("Couldn't get USB Online status, rc=%d\n", rc); + + if (!pval.intval) { + smblib_dbg(chg, PR_OEM, "usb not online, don't set thermal\n"); + return 0; + } + + if (chg->system_temp_level >= (chg->thermal_levels - 1)) +#else if (chg->system_temp_level == chg->thermal_levels) +#endif return vote(chg->chg_disable_votable, THERMAL_DAEMON_VOTER, true, 0); vote(chg->chg_disable_votable, THERMAL_DAEMON_VOTER, false, 0); + +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->pd_active == POWER_SUPPLY_PD_PPS_ACTIVE) + schedule_delayed_work(&chg->thermal_setting_work, 3 * HZ); + else + smblib_therm_charging(chg); +#else 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]); +#endif + return 0; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_set_prop_battery_charging_enabled(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int icl = 0; + if (chg->is_qc_class_a && !chg->qc3_raise_done) + icl = MAIN_ICL_MIN; + + if (val->intval == 0) { + if (chg->six_pin_step_charge_enable) { + vote(chg->usb_icl_votable, MAIN_ICL_MIN_VOTER, + true, MAIN_ICL_MIN); + } + else { + if (chg->power_good_en) + vote(chg->usb_icl_votable, MAIN_CHG_SUSPEND_VOTER, + true, MAIN_CHG_SUSPEND_ICL); + else + vote(chg->usb_icl_votable, MAIN_CHG_SUSPEND_VOTER, + true, icl); + } +#ifndef CONFIG_FUEL_GAUGE_BQ27Z561 + schedule_delayed_work(&chg->reduce_fcc_work, + msecs_to_jiffies(ESR_WORK_TIME_97S)); +#endif + + } else { + if (chg->six_pin_step_charge_enable) + vote(chg->usb_icl_votable, MAIN_ICL_MIN_VOTER, + false, 0); + else + vote(chg->usb_icl_votable, MAIN_CHG_SUSPEND_VOTER, + false, 0); +#ifndef CONFIG_FUEL_GAUGE_BQ27Z561 + if (is_client_vote_enabled(chg->fcc_votable, ESR_WORK_VOTER)) + vote(chg->fcc_votable, ESR_WORK_VOTER, false, 0); + cancel_delayed_work_sync(&chg->reduce_fcc_work); +#endif + } + + return 0; +} + +int smblib_get_prop_type_recheck(struct smb_charger *chg, + union power_supply_propval *val) +{ + int status = 0; + + if (chg->recheck_charger) + status |= BIT(0) << 8; + + status |= chg->precheck_charger_type << 4; + status |= chg->real_charger_type; + + val->intval = status; + + return 0; +} + +int smblib_set_prop_type_recheck(struct smb_charger *chg, + const union power_supply_propval *val) +{ + if (val->intval == 0) { + cancel_delayed_work_sync(&chg->charger_type_recheck); + chg->recheck_charger = false; + } + return 0; +} + +int smblib_night_charging_func(struct smb_charger *chg, + union power_supply_propval *val) +{ + union power_supply_propval pval = {0, }; + static int pre_night_chg_flag = 0; + int capacity, battery_input_suspend; + int rc = 0; + + rc = smblib_get_prop_batt_capacity(chg, &pval); + capacity = pval.intval; + rc = smblib_get_prop_battery_input_suspend(chg, &pval); + battery_input_suspend = pval.intval; + pr_info("nchg_func:capacity:%d, battery_input_suspend:%d.\n", + capacity, battery_input_suspend); + pr_info("nchg_func:pre_nchg:%d, nchg:%d.\n", + pre_night_chg_flag, chg->night_chg_flag); + + if (pre_night_chg_flag != chg->night_chg_flag) { + if (chg->night_chg_flag && capacity >= 80) { + pval.intval = 1; + rc = smblib_set_prop_battery_input_suspend(chg, &pval); + pr_err("nchg_func:opne night charging.\n"); + pre_night_chg_flag = chg->night_chg_flag; + } + } + + if (battery_input_suspend && + (!chg->night_chg_flag || capacity <= 75)) { + pval.intval = 0; + smblib_set_prop_battery_input_suspend(chg, &pval); + pr_err("nchg_func:close night charging.\n"); + pre_night_chg_flag = 0; + } + + val->intval = chg->night_chg_flag; + + return 0; +} + +#define PERIPHERAL_MASK 0xFF +static u16 peripheral_base; +static char log[256] = ""; +static char version[8] = "smb:01:"; +static inline void dump_reg(struct smb_charger *chg, u16 addr, + const char *name) +{ + u8 reg; + int rc; + char reg_data[50] = ""; + + if (NULL == name) { + strlcat(log, "\n", sizeof(log)); + printk(log); + return; + } + + rc = smblib_read(chg, addr, ®); + if (rc < 0) + smblib_err(chg, "Couldn't read OTG status rc=%d\n", rc); + /* print one peripheral base registers in one line */ + if (peripheral_base != (addr & ~PERIPHERAL_MASK)) { + peripheral_base = addr & ~PERIPHERAL_MASK; + memset(log, 0, sizeof(log)); + snprintf(reg_data, sizeof(reg_data), "%s%04x ", version, peripheral_base); + strlcat(log, reg_data, sizeof(log)); + } + memset(reg_data, 0, sizeof(reg_data)); + snprintf(reg_data, sizeof(reg_data), "%02x ", reg); + strlcat(log, reg_data, sizeof(log)); + + smblib_dbg(chg, PR_REGISTER, "%s - %04X = %02X\n", + name, addr, reg); +} + +static void dump_regs(struct smb_charger *chg) +{ + u16 addr; + + /* charger peripheral */ + for (addr = 0x6; addr <= 0xE; addr++) + dump_reg(chg, CHGR_BASE + addr, "CHGR Status"); + + for (addr = 0x10; addr <= 0x1B; addr++) + dump_reg(chg, CHGR_BASE + addr, "CHGR INT"); + + for (addr = 0x50; addr <= 0x70; addr++) + dump_reg(chg, CHGR_BASE + addr, "CHGR Config"); + + dump_reg(chg, CHGR_BASE + addr, NULL); + + for (addr = 0x10; addr <= 0x1B; addr++) + dump_reg(chg, BATIF_BASE + addr, "BATIF INT"); + + for (addr = 0x50; addr <= 0x52; addr++) + dump_reg(chg, BATIF_BASE + addr, "BATIF Config"); + + for (addr = 0x60; addr <= 0x62; addr++) + dump_reg(chg, BATIF_BASE + addr, "BATIF Config"); + + for (addr = 0x70; addr <= 0x71; addr++) + dump_reg(chg, BATIF_BASE + addr, "BATIF Config"); + + dump_reg(chg, BATIF_BASE + addr, NULL); + + for (addr = 0x6; addr <= 0x10; addr++) + dump_reg(chg, USBIN_BASE + addr, "USBIN Status"); + + for (addr = 0x12; addr <= 0x19; addr++) + dump_reg(chg, USBIN_BASE + addr, "USBIN INT "); + + for (addr = 0x40; addr <= 0x43; addr++) + dump_reg(chg, USBIN_BASE + addr, "USBIN Cmd "); + + for (addr = 0x58; addr <= 0x70; addr++) + dump_reg(chg, USBIN_BASE + addr, "USBIN Config "); + + for (addr = 0x80; addr <= 0x84; addr++) + dump_reg(chg, USBIN_BASE + addr, "USBIN Config "); + + dump_reg(chg, USBIN_BASE + addr, NULL); + + for (addr = 0x06; addr <= 0x1B; addr++) + dump_reg(chg, TYPEC_BASE + addr, "TYPEC Status"); + + for (addr = 0x42; addr <= 0x72; addr++) + dump_reg(chg, TYPEC_BASE + addr, "TYPEC Config"); + + dump_reg(chg, TYPEC_BASE + 0x44, "TYPEC MODE CFG"); + dump_reg(chg, TYPEC_BASE + addr, NULL); + + for (addr = 0x6; addr <= 0x10; addr++) + dump_reg(chg, MISC_BASE + addr, "MISC Status"); + + for (addr = 0x15; addr <= 0x1B; addr++) + dump_reg(chg, MISC_BASE + addr, "MISC INT"); + + for (addr = 0x51; addr <= 0x62; addr++) + dump_reg(chg, MISC_BASE + addr, "MISC Config"); + + for (addr = 0x70; addr <= 0x76; addr++) + dump_reg(chg, MISC_BASE + addr, "MISC Config"); + + for (addr = 0x80; addr <= 0x84; addr++) + dump_reg(chg, MISC_BASE + addr, "MISC Config"); + + for (addr = 0x90; addr <= 0x94; addr++) + dump_reg(chg, MISC_BASE + addr, "MISC Config"); + + dump_reg(chg, MISC_BASE + addr, NULL); +} + +#define CHARGING_PERIOD_S 600 +#define NOT_CHARGING_PERIOD_S 1200 +static void smblib_reg_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + reg_work.work); + int rc, usb_present; + union power_supply_propval val; + int icl_settle, usb_cur_in, usb_vol_in, icl_sts; + int charger_type, typec_mode, typec_orientation, esr_uohms_nominal, esr_uohms_actual, resistance; + + dump_regs(chg); + rc = smblib_get_prop_usb_present(chg, &val); + if (rc < 0) { + pr_err("Couldn't get usb present rc=%d\n", rc); + schedule_delayed_work(&chg->reg_work, + NOT_CHARGING_PERIOD_S * HZ); + return; + } + usb_present = val.intval; + + smblib_dbg(chg, PR_OEM, "AWAKE vote value is %d voted by %s\n", + get_effective_result(chg->awake_votable), + get_effective_client(chg->awake_votable)); + + if (usb_present) { + smblib_dbg(chg, PR_OEM, "ICL vote value is %d voted by %s\n", + get_effective_result(chg->usb_icl_votable), + get_effective_client(chg->usb_icl_votable)); + smblib_dbg(chg, PR_OEM, "FCC vote value is %d voted by %s\n", + get_effective_result(chg->fcc_votable), + get_effective_client(chg->fcc_votable)); + smblib_dbg(chg, PR_OEM, "FV vote value is %d voted by %s\n", + get_effective_result(chg->fv_votable), + get_effective_client(chg->fv_votable)); + + power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, + &val); + usb_cur_in = val.intval; + + power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + &val); + usb_vol_in = val.intval; + + power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, + &val); + icl_settle = val.intval; + + power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_REAL_TYPE, + &val); + charger_type = val.intval; + + power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_TYPEC_MODE, + &val); + typec_mode = val.intval; + + power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION, + &val); + typec_orientation = val.intval; + + power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_RESISTANCE, + &val); + resistance= val.intval; + + power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_ESR_NOMINAL, + &val); + esr_uohms_nominal= val.intval; + + power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_ESR_ACTUAL, + &val); + esr_uohms_actual= val.intval; + + smblib_dbg(chg, PR_OEM, "ICL settle value[%d], usbin adc current[%d], vbusin adc vol[%d]\n, " + "resistance [%d], esr_nominal [%d], esr_actual [%d]\n",icl_settle, usb_cur_in, + usb_vol_in, resistance, esr_uohms_nominal, esr_uohms_actual); + if (!chg->usb_main_psy) { + chg->usb_main_psy = power_supply_get_by_name("main"); + } + else { + power_supply_get_property(chg->usb_main_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, + &val); + icl_sts = val.intval; + smblib_dbg(chg, PR_OEM, "AICL_STS[%d]\n", icl_sts); + } + + smblib_dbg(chg, PR_OEM, + "Type-C orientation[%d], Type-C mode[%d], Real Charger Type[%d]\n", + typec_orientation, typec_mode, charger_type); + + schedule_delayed_work(&chg->reg_work, + CHARGING_PERIOD_S * HZ); + } else { + schedule_delayed_work(&chg->reg_work, + NOT_CHARGING_PERIOD_S * HZ); + } +} +#endif + int smblib_set_prop_input_current_limited(struct smb_charger *chg, const union power_supply_propval *val) { @@ -2436,6 +4128,15 @@ int smblib_set_prop_rechg_soc_thresh(struct smb_charger *chg, int rc; u8 new_thr = DIV_ROUND_CLOSEST(val->intval * 255, 100); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* + * As DIV_ROUND_CLOSEST cal cause new_thr to 252, we add 1 more to + * improve recharging UI soc still to 100% to improve user experience. + */ + if (val->intval == RECHARGE_SOC_THR) + new_thr += 1; +#endif + rc = smblib_write(chg, CHARGE_RCHG_SOC_THRESHOLD_CFG_REG, new_thr); if (rc < 0) { @@ -2572,6 +4273,13 @@ static void smblib_hvdcp_adaptive_voltage_change(struct smb_charger *chg) u8 stat; if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP) { +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->qc2_unsupported) { + smblib_hvdcp_set_fsw(chg, QC_5V_BIT); + power_supply_changed(chg->usb_main_psy); + return; + } +#endif rc = smblib_read(chg, QC_CHANGE_STATUS_REG, &stat); if (rc < 0) { smblib_err(chg, @@ -2585,9 +4293,11 @@ static void smblib_hvdcp_adaptive_voltage_change(struct smb_charger *chg) if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3 || chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3P5) { +#ifndef CONFIG_MACH_XIAOMI_SM8250 rc = smblib_hvdcp3_set_fsw(chg); if (rc < 0) smblib_err(chg, "Couldn't set QC3.0 Fsw rc=%d\n", rc); +#endif } power_supply_changed(chg->usb_main_psy); @@ -2599,8 +4309,22 @@ int smblib_dp_dm(struct smb_charger *chg, int val) union power_supply_propval pval; u8 stat; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->use_bq_pump) { + pr_info("dp_dm is controled by our self\n"); + return rc; + } + /* if raise_vbus work is running, ignore dp_dm pulses */ + if (chg->raise_vbus_to_detect) + return rc; +#endif + switch (val) { case POWER_SUPPLY_DP_DM_DP_PULSE: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->pulse_cnt > MAX_PLUSE_COUNT_ALLOWED) + return rc; +#endif /* * Pre-emptively increment pulse count to enable the setting * of FSW prior to increasing voltage. @@ -2624,6 +4348,21 @@ int smblib_dp_dm(struct smb_charger *chg, int val) smblib_dbg(chg, PR_PARALLEL, "DP_DM_DP_PULSE rc=%d cnt=%d\n", rc, chg->pulse_cnt); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* + * if use class_a qc, and slave ic is charge pump, should limit + * maxium icl to 1.9A, as VBUS will raise to about 9V to 9.8V, + * so we set icl to 1.9A when vbus raise above 7.4V + */ + if (chg->is_qc_class_a && chg->sec_cp_present) { + if (chg->pulse_cnt >= HIGH_NUM_PULSE_THR + && !chg->high_vbus_detected) { + vote(chg->usb_icl_votable, QC_A_CP_ICL_MAX_VOTER, true, + HVDCP_CLASS_A_FOR_CP_UA); + chg->high_vbus_detected = true; + } + } +#endif break; case POWER_SUPPLY_DP_DM_DM_PULSE: rc = smblib_dm_pulse(chg); @@ -2631,6 +4370,20 @@ int smblib_dp_dm(struct smb_charger *chg, int val) chg->pulse_cnt--; smblib_dbg(chg, PR_PARALLEL, "DP_DM_DM_PULSE rc=%d cnt=%d\n", rc, chg->pulse_cnt); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* + * if use class_a qc, and slave ic is charge pump, should restore + * icl to 2.8A when charge pump is not working for class_a qc + */ + if (chg->is_qc_class_a && chg->sec_cp_present) { + if (chg->pulse_cnt < HIGH_NUM_PULSE_THR + && chg->high_vbus_detected) { + vote(chg->usb_icl_votable, QC_A_CP_ICL_MAX_VOTER, false, + 0); + chg->high_vbus_detected = false; + } + } +#endif break; case POWER_SUPPLY_DP_DM_ICL_DOWN: target_icl_ua = get_effective_result(chg->usb_icl_votable); @@ -2668,6 +4421,10 @@ int smblib_dp_dm(struct smb_charger *chg, int val) pr_err("Failed to force 5V\n"); break; case POWER_SUPPLY_DP_DM_FORCE_9V: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* we use our own qc2 method to raise to 9V, so just return here */ + return 0; +#endif if (chg->qc2_unsupported_voltage == QC2_NON_COMPLIANT_9V) { smblib_err(chg, "Couldn't set 9V: unsupported\n"); return -EINVAL; @@ -2693,6 +4450,10 @@ int smblib_dp_dm(struct smb_charger *chg, int val) pr_err("Failed to force 9V\n"); break; case POWER_SUPPLY_DP_DM_FORCE_12V: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* we use our own qc2 method to raise to 12V, so just return here */ + return 0; +#endif if (chg->qc2_unsupported_voltage == QC2_NON_COMPLIANT_12V) { smblib_err(chg, "Couldn't set 12V: unsupported\n"); return -EINVAL; @@ -2729,6 +4490,129 @@ int smblib_dp_dm(struct smb_charger *chg, int val) return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_dp_dm_bq(struct smb_charger *chg, int val) +{ + int rc = 0, qc3p5_dp_cnt = 0; + + /* if raise_vbus work is running, ignore dp_dm pulses */ + if (chg->raise_vbus_to_detect) + return rc; + + switch (val) { + case POWER_SUPPLY_DP_DM_DP_PULSE: + /* + * if hvdcp_opti wrongly send more than 30 dp pulse(11V) to smb5, + * ignore them to allow maxium vbus as 11V, as charge pump do not + * need the vin more than 11V, and protect the device. + */ + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3 + && chg->pulse_cnt > MAX_PLUSE_COUNT_ALLOWED) { + return rc; + } else if (chg->qc3p5_supported + && chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3P5) { + if (chg->pulse_cnt > MAX_QC3P5_PLUSE_COUNT_ALLOWED) + return rc; + else if (chg->pulse_cnt >= QC3P5_DP_RAPIDLY_TUNE_ALLOWED) + chg->qc3p5_dp_tune_rapidly = false; + else + chg->qc3p5_dp_tune_rapidly = true; + } + + /* + * Pre-emptively increment pulse count to enable the setting + * of FSW prior to increasing voltage. + */ + if (chg->qc3p5_dp_tune_rapidly) + chg->pulse_cnt += QC3P5_DP_RAPIDLY_TUNE_PULSE; + else + chg->pulse_cnt++; + + rc = smblib_hvdcp3_set_fsw(chg); + if (rc < 0) + smblib_err(chg, "Couldn't set QC3.0 Fsw rc=%d\n", rc); + + if (chg->qc3p5_dp_tune_rapidly) { + for (qc3p5_dp_cnt = 1; qc3p5_dp_cnt <= QC3P5_DP_RAPIDLY_TUNE_PULSE; qc3p5_dp_cnt++) { + rc = smblib_dp_pulse(chg); + if (rc < 0) { + smblib_err(chg, "Couldn't increase pulse count rc=%d\n", rc); + chg->pulse_cnt--; + } + smblib_dbg(chg, PR_PARALLEL, "DP_DM_DP_PULSE rapidly rc=%d cnt=%d\n", + rc, chg->pulse_cnt - QC3P5_DP_RAPIDLY_TUNE_PULSE + qc3p5_dp_cnt); + usleep_range(8000, 8010); // delay 8ms + } + } else { + rc = smblib_dp_pulse(chg); + if (rc < 0) { + smblib_err(chg, "Couldn't increase pulse count rc=%d\n", + rc); + /* + * Increment pulse count failed; + * reset to former value. + */ + chg->pulse_cnt--; + } + + smblib_dbg(chg, PR_PARALLEL, "DP_DM_DP_PULSE rc=%d cnt=%d\n", + rc, chg->pulse_cnt); + } + /* + * if use class_a qc, and slave ic is charge pump, should limit + * maxium icl to 1.9A, as VBUS will raise to about 9V to 9.8V, + * so we set icl to 1.9A when vbus raise above 7.4V + */ + if (chg->is_qc_class_a && chg->use_bq_pump) { + if (chg->pulse_cnt >= HIGH_NUM_PULSE_THR + && !chg->high_vbus_detected) { + vote(chg->usb_icl_votable, QC_A_CP_ICL_MAX_VOTER, true, + HVDCP_CLASS_A_FOR_CP_UA); + chg->high_vbus_detected = true; + } + } + break; + + case POWER_SUPPLY_DP_DM_DM_PULSE: + rc = smblib_dm_pulse(chg); + if (!rc && chg->pulse_cnt) + chg->pulse_cnt--; + smblib_dbg(chg, PR_PARALLEL, "DP_DM_DM_PULSE rc=%d cnt=%d\n", + rc, chg->pulse_cnt); + /* + * if use class_a qc, and slave ic is charge pump, should restore + * icl to 2.8A when charge pump is not working for class_a qc + */ + if (chg->is_qc_class_a && chg->use_bq_pump) { + if (chg->pulse_cnt < HIGH_NUM_PULSE_THR + && chg->high_vbus_detected) { + vote(chg->usb_icl_votable, QC_A_CP_ICL_MAX_VOTER, false, + 0); + chg->high_vbus_detected = false; + } + } + break; + + case POWER_SUPPLY_DP_DM_FORCE_5V: + rc = smblib_force_vbus_voltage(chg, FORCE_5V_BIT); + chg->pulse_cnt = 0; + if (rc < 0) + pr_err("Failed to force 5V\n"); + break; + case POWER_SUPPLY_DP_DM_RAISE_VOLT_DONE: + chg->qc3_raise_done = true; + break; + case POWER_SUPPLY_DP_DM_RAISE_VOLT_START: + chg->qc3_raise_done = false; + break; + default: + break; + } + + return rc; +} +#endif + int smblib_disable_hw_jeita(struct smb_charger *chg, bool disable) { int rc; @@ -2737,10 +4621,15 @@ int smblib_disable_hw_jeita(struct smb_charger *chg, bool disable) /* * Disable h/w base JEITA compensation if s/w JEITA is enabled */ +#ifdef CONFIG_FUEL_GAUGE_BQ27Z561 + /*J1 use ti gauge disable all hard jeita, J2 use qcom default jeita */ + mask = 0xFF; +#else mask = JEITA_EN_COLD_SL_FCV_BIT | JEITA_EN_HOT_SL_FCV_BIT | JEITA_EN_HOT_SL_CCC_BIT | JEITA_EN_COLD_SL_CCC_BIT, +#endif rc = smblib_masked_write(chg, JEITA_EN_CFG_REG, mask, disable ? 0 : mask); if (rc < 0) { @@ -2877,6 +4766,335 @@ static int smblib_update_thermal_readings(struct smb_charger *chg) #define THERM_REG_RECHECK_DELAY_1S 1000 /* 1 sec */ #define THERM_REG_RECHECK_DELAY_8S 8000 /* 8 sec */ +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define THERM_REG_RECHECK_DELAY_200MS 200 /* 200 msec */ +#define THERM_REG_RECHECK_DELAY_5S 5000 /* 5 sec */ +#define THERM_REG_RECHECK_DELAY_10S 10000 /* 10 sec */ +#define CONNECTOR_THERM_ABOVE 200 /* 20 Dec */ +#define CONNECTOR_THERM_HIG 500 /* 50 Dec */ +#define CONNECTOR_THERM_TOO_HIG 700 /* 70 Dec */ + +int smblib_set_vbus_disable(struct smb_charger *chg, + bool disable) +{ + int ret; + + smblib_dbg(chg, PR_OEM, "set vbus disable:%d\n", disable); + if (disable) { + if (chg->vbus_disable_gpio) { + gpio_set_value(chg->vbus_disable_gpio, 1); + } + } else { + if (chg->vbus_disable_gpio) { + gpio_set_value(chg->vbus_disable_gpio, 0); + } + } + chg->vbus_disable = disable; + + return ret; +} + +static int smblib_set_sw_conn_therm_regulation(struct smb_charger *chg, + bool enable) +{ + int rc = 0; + + if (!chg->support_conn_therm) + return rc; + + if (enable) { + chg->entry_time = ktime_get(); + schedule_delayed_work(&chg->conn_therm_work, + msecs_to_jiffies(THERM_REG_RECHECK_DELAY_1S)); + } else { + if (chg->thermal_status != TEMP_ABOVE_RANGE) + cancel_delayed_work(&chg->conn_therm_work); + } + + return rc; +} + +#define AFTER_RAISE_VBUS_CHECK_DELAY_US 300000 /* 300 msec */ +#define AFTER_DETCH_CHECK_DELAY_US 650000 /* 65 msec */ +#define FAKE_PLUG_OUT_CHECK_DELAY_MS 900 /* 900 Msec */ +static void smblib_plugin_check_time_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + plugin_check_time_work); + u64 plugin_time; + u64 delta_us; + + plugin_time = ktime_get(); + if (!chg->vbus_rising) { + /*plug out */ + delta_us = ktime_us_delta(plugin_time, chg->after_raise_vbus_time); + if (delta_us > AFTER_RAISE_VBUS_CHECK_DELAY_US) { + chg->fake_plug_out = false; + } else { + chg->fake_plug_out = true; + chg->plugin_detch_check_time = plugin_time; + schedule_delayed_work(&chg->fake_plug_out_check_work, + msecs_to_jiffies(FAKE_PLUG_OUT_CHECK_DELAY_MS)); + smblib_dbg(chg, PR_OEM, "%s fake plug out delta_us:%d \n", __func__, delta_us); + } + + if (chg->fake_plug_out == false) + smblib_update_usb_type(chg); + } else { + /*plug in*/ + delta_us = ktime_us_delta(plugin_time, chg->plugin_detch_check_time); + if (delta_us > AFTER_DETCH_CHECK_DELAY_US ) { + chg->no_raise_vbus_status = false; + return; + } else { + chg->plugin_attach_check_time = plugin_time; + if (chg->fake_plug_out == true) + chg->no_raise_vbus_status = true; + smblib_dbg(chg, PR_OEM, "%s in delta_us:%d \n", __func__, delta_us); + } + } +} + +static void smblib_fake_plug_out_check_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + fake_plug_out_check_work.work); + + chg->fake_plug_out = false; + power_supply_changed(chg->usb_psy); + smblib_dbg(chg, PR_OEM, "%s clean fake_plug_out \n", __func__); +} + +static void smblib_clean_cp_to_sw_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + clean_cp_to_sw_work.work); + + chg->cp_to_sw_status = false; + smblib_dbg(chg, PR_OEM, "%s clean cp_to_sw_status \n", __func__); +} + +#define FFC_DISABLE_CHG_DELAY_US 120000000 /* 120 sec */ +#define FFC_DISABLE_CHG_RECHECK_DELAY_1S 1000 /* 1 sec */ +#define FFC_DISABLE_CHG_RECHECK_DELAY_10S 10000 /* 10 sec */ +#define FFC_DISABLE_CHG_ENABLE_DELAY_120S 120000 /* 120 sec */ +#define FFC_DISABLE_CHG_CHECK_TIME 5 +#define BATTERY_REPORT_FULL_CURRENT -400000 + +static void smblib_after_ffc_chg_dis_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + after_ffc_chg_dis_work.work); + union power_supply_propval pval = {0, }; + int rc = 0; + u64 delta_us; + static int count; + + if (!chg->last_ffc_remove_time) + return; + + smblib_dbg(chg, PR_OEM, "delta_us :%d\n", delta_us / 1000000); + + delta_us = ktime_us_delta(ktime_get(), chg->last_ffc_remove_time); + if (delta_us > FFC_DISABLE_CHG_DELAY_US) + return; + + rc = smblib_get_prop_usb_present(chg, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't get usb present rc = %d\n", rc); + return; + } + if (!pval.intval) { + vote(chg->chg_disable_votable, AFTER_FFC_VOTER, false, 0); + count = 0; + return; + } + + rc = smblib_get_prop_from_bms(chg, + POWER_SUPPLY_PROP_CAPACITY, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't read SOC value, rc=%d\n", rc); + return; + } + if (pval.intval < 90) + return; + + rc = smblib_get_prop_from_bms(chg, + POWER_SUPPLY_PROP_CURRENT_NOW, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't read SOC value, rc=%d\n", rc); + return; + } + if (pval.intval < 0 && pval.intval > BATTERY_REPORT_FULL_CURRENT) { + if (count < FFC_DISABLE_CHG_CHECK_TIME) { + count++; + schedule_delayed_work(&chg->after_ffc_chg_dis_work, + msecs_to_jiffies(FFC_DISABLE_CHG_RECHECK_DELAY_1S)); + } else { + smblib_dbg(chg, PR_OEM, "disable chg for :%ds when ffc charging\n", + FFC_DISABLE_CHG_ENABLE_DELAY_120S - (delta_us / 1000)); + vote(chg->chg_disable_votable, AFTER_FFC_VOTER, true, 0); + schedule_delayed_work(&chg->after_ffc_chg_en_work, + msecs_to_jiffies(FFC_DISABLE_CHG_ENABLE_DELAY_120S - (delta_us / 1000))); + } + } else { + count = 0; + } + + return; +} + +static void smblib_after_ffc_chg_en_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + after_ffc_chg_en_work.work); + + vote(chg->chg_disable_votable, AFTER_FFC_VOTER, false, 0); + + return; +} + +#define TEMP_READ_RETRY 3 +int smblib_get_prop_connector_temp(struct smb_charger *chg) +{ + int rc, retry = 0; + + rc = smblib_read_iio_channel(chg, chg->iio.connector_temp_chan, + DIV_FACTOR_DECIDEGC, &chg->connector_temp); + if (rc < 0) { + while (retry < TEMP_READ_RETRY) { + rc = smblib_read_iio_channel(chg, chg->iio.connector_temp_chan, + DIV_FACTOR_DECIDEGC, &chg->connector_temp); + if (rc >= 0) { + retry = 0; + break; + } else { + retry++; + } + } + if (retry >= TEMP_READ_RETRY) { + smblib_err(chg, "Couldn't read CONN TEMP channel, rc=%d\n", rc); + return rc; + } + } + + return chg->connector_temp; +} + +#define CHG_DETECT_CONN_THERM_US 120000000 /* 120sec */ +static void smblib_conn_therm_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + conn_therm_work.work); + int rc = 0, wdog_timeout = THERM_REG_RECHECK_DELAY_10S; + static int thermal_status = TEMP_BELOW_RANGE; + union power_supply_propval val = {0, }; + int usb_present; + u64 elapsed_us; + + rc = smblib_get_prop_connector_temp(chg); + if (rc < 0) + smblib_err(chg, "Couldn't read CONN TEMP channel, rc=%d\n", rc); + + if (chg->fake_conn_temp != 0) + chg->connector_temp = chg->fake_conn_temp; + + if (chg->connector_temp >= CONNECTOR_THERM_TOO_HIG) { + smblib_dbg(chg, PR_OEM, "chg->connector_temp:%d is too hig\n", chg->connector_temp); + thermal_status = TEMP_ABOVE_RANGE; + wdog_timeout = THERM_REG_RECHECK_DELAY_1S; + } else if (chg->connector_temp >= CONNECTOR_THERM_HIG) { + if ((thermal_status == TEMP_ABOVE_RANGE) && + (chg->connector_temp > CONNECTOR_THERM_TOO_HIG - 100)) { + smblib_dbg(chg, PR_OEM, "chg->connector_temp:%d is warm\n", chg->connector_temp); + } else { + thermal_status = TEMP_ALERT_LEVEL; + } + wdog_timeout = THERM_REG_RECHECK_DELAY_1S; + } else { + thermal_status = TEMP_BELOW_RANGE; + wdog_timeout = THERM_REG_RECHECK_DELAY_10S; + } + + smblib_dbg(chg, PR_OEM, "CONN TEMP thermal_status=%d, chip->thermal_status=%d, connect_temp= %d\n", thermal_status, chg->thermal_status, chg->connector_temp); + if (thermal_status != chg->thermal_status) { + chg->thermal_status = thermal_status; + if (thermal_status == TEMP_ABOVE_RANGE) { + smblib_dbg(chg, PR_OEM, "connect temp is too hot, disable vbus\n"); + vote(chg->usb_icl_votable, SW_CONN_THERM_VOTER, true, 0); + smblib_set_vbus_disable(chg, true); + } else { + smblib_dbg(chg, PR_OEM, "connect temp normal recovery vbus\n"); + vote(chg->usb_icl_votable, SW_CONN_THERM_VOTER, false, 0); + smblib_set_vbus_disable(chg, false); + } + } + + elapsed_us = ktime_us_delta(ktime_get(), chg->entry_time); + if (elapsed_us < CHG_DETECT_CONN_THERM_US) + wdog_timeout = THERM_REG_RECHECK_DELAY_200MS; + + smblib_get_prop_usb_present(chg, &val); + usb_present = val.intval; + + if (!usb_present && thermal_status == TEMP_BELOW_RANGE) { + smblib_dbg(chg, PR_OEM, "usb is disconnet cancel the connect them work\n"); + return; + } else + schedule_delayed_work(&chg->conn_therm_work, + msecs_to_jiffies(wdog_timeout)); + + return; +} + +static void smblib_report_soc_decimal_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + report_soc_decimal_work.work); + int quick_charge_type; + + quick_charge_type = smblib_get_quick_charge_type(chg); + + if (QUICK_CHARGE_TURBE == quick_charge_type) + power_supply_changed(chg->bms_psy); +} + +static void smblib_step_charge_notify_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + step_charge_notify_work.work); + int input_present, capacity, rc; + union power_supply_propval pval = {0, }; + + rc = smblib_is_input_present(chg, &input_present); + if (rc < 0) + return; + + if (input_present == INPUT_NOT_PRESENT) + return; + + rc = smblib_get_prop_batt_capacity(chg, &pval); + if (rc < 0) { + pr_err("Couldn't get batt charge type rc=%d\n", rc); + return; + } + capacity = pval.intval; + + /* high capacity no need notify qcom step charge callback */ + if (capacity >= 90) + return; + + if (input_present & INPUT_PRESENT_DC + || input_present & INPUT_PRESENT_USB) { + /* notify qcom step charge callback to handle step charge every 2s */ + if (chg->batt_psy) + power_supply_changed(chg->batt_psy); + schedule_delayed_work(&chg->step_charge_notify_work, + msecs_to_jiffies(NOTIFY_STEP_CALLBACK_MS)); + } +} +#endif + static int smblib_process_thermal_readings(struct smb_charger *chg) { int rc = 0, wdog_timeout = SNARL_WDOG_TMOUT_8S; @@ -3020,6 +5238,11 @@ int smblib_get_prop_voltage_wls_output(struct smb_charger *chg, { int rc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->wireless_bq) + return rc; +#endif + if (!chg->wls_psy) { chg->wls_psy = power_supply_get_by_name("wireless"); if (!chg->wls_psy) @@ -3053,6 +5276,16 @@ int smblib_get_prop_dc_present(struct smb_charger *chg, return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->wireless_bq) { + if (chg->reverse_gpio_state == REVERSE_GPIO_STATE_START + ||chg->reverse_gpio_state == REVERSE_GPIO_STATE_END){ + val->intval = 0; + } else { + val->intval = (bool)(stat & DCIN_PON_RT_STS_BIT); + } + } else +#endif val->intval = (bool)(stat & DCIN_PLUGIN_RT_STS_BIT); return 0; } @@ -3073,6 +5306,22 @@ int smblib_get_prop_dc_online(struct smb_charger *chg, return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->report_input_absent) { + val->intval = 0; + return rc; + } + + if (chg->wireless_bq) { + if (chg->fake_dc_on){ + val->intval = true; + return rc; + } + rc = smblib_get_prop_dc_present(chg, val); + return rc; + } +#endif + if (is_client_vote_enabled(chg->dc_suspend_votable, CHG_TERMINATION_VOTER)) { rc = smblib_get_prop_dc_present(chg, val); @@ -3128,6 +5377,11 @@ int smblib_get_prop_dc_voltage_now(struct smb_charger *chg, { int rc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->wireless_bq) + return rc; +#endif + if (!chg->wls_psy) { chg->wls_psy = power_supply_get_by_name("wireless"); if (!chg->wls_psy) @@ -3154,6 +5408,12 @@ int smblib_set_prop_dc_current_max(struct smb_charger *chg, const union power_supply_propval *val) { chg->dcin_icl_user_set = true; + +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->wireless_bq) + return 0; +#endif + return smblib_set_charge_param(chg, &chg->param.dc_icl, val->intval); } @@ -3163,6 +5423,11 @@ int smblib_set_prop_voltage_wls_output(struct smb_charger *chg, { int rc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->wireless_bq) + return rc; +#endif + if (!chg->wls_psy) { chg->wls_psy = power_supply_get_by_name("wireless"); if (!chg->wls_psy) @@ -3198,6 +5463,11 @@ int smblib_set_prop_dc_reset(struct smb_charger *chg) { int rc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->wireless_bq) + return rc; +#endif + rc = vote(chg->dc_suspend_votable, VOUT_VOTER, true, 0); if (rc < 0) { smblib_err(chg, "Couldn't suspend DC rc=%d\n", rc); @@ -3271,11 +5541,80 @@ int smblib_get_prop_usb_online(struct smb_charger *chg, int rc = 0; u8 stat; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int usb_present; + union power_supply_propval pval = {0, }; + bool dc_power_on; + int icl = 0; + if (chg->is_qc_class_a && !chg->qc3_raise_done) + icl = MAIN_ICL_MIN; + + if (chg->ext_bbc) { + rc = smblib_get_prop_from_bbc(chg, + POWER_SUPPLY_PROP_PRESENT, &pval); + if (rc < 0) + val->intval = false; + val->intval = pval.intval; + return 0; + } +#endif + if (get_client_vote_locked(chg->usb_icl_votable, USER_VOTER) == 0) { val->intval = false; return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->wireless_bq) { + rc = smblib_get_prop_dc_present(chg, &pval); + dc_power_on = pval.intval; + if (dc_power_on == 1) { + val->intval = false; + return rc; + } + } + + rc = smblib_get_prop_usb_present(chg, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't get usb present rc = %d\n", rc); + return rc; + } + + usb_present = pval.intval; + if (usb_present && + get_client_vote_locked(chg->usb_icl_votable, JEITA_VOTER) == 0) { + /* show online when JEITA_VOTER 0mA is vote to improve user experience */ + val->intval = true; + return rc; + } + + if (chg->fake_plug_out == true || chg->cp_to_sw_status == true) { + val->intval = true; + return rc; + } + + if (chg->use_bq_pump && !chg->six_pin_step_charge_enable + && (get_client_vote_locked(chg->usb_icl_votable, + MAIN_CHG_SUSPEND_VOTER) == icl)) { + val->intval = true; + return rc; + } + + if (chg->use_bq_pump && chg->six_pin_step_charge_enable + && (get_client_vote_locked(chg->usb_icl_votable, + MAIN_ICL_MIN_VOTER) == MAIN_ICL_MIN)) { + val->intval = true; + return rc; + } + + if (chg->use_bq_pump && !chg->six_pin_step_charge_enable + && is_client_vote_enabled_locked(chg->chg_disable_votable, + AFTER_FFC_VOTER)) { + val->intval = true; + return rc; + } +#endif + if (is_client_vote_enabled_locked(chg->usb_icl_votable, CHG_TERMINATION_VOTER)) { rc = smblib_get_prop_usb_present(chg, val); @@ -3293,6 +5632,18 @@ int smblib_get_prop_usb_online(struct smb_charger *chg, val->intval = (stat & USE_USBIN_BIT) && (stat & VALID_INPUT_POWER_SOURCE_STS_BIT); + +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* power good is on and sink pluged-in, do not report online */ + if (val->intval + && chg->power_good_en + && chg->typec_mode >= POWER_SUPPLY_TYPEC_SINK + && chg->typec_mode <= POWER_SUPPLY_TYPEC_POWERED_CABLE_ONLY) { + val->intval = 0; + return rc; + } +#endif + return rc; } @@ -3301,6 +5652,13 @@ int smblib_get_usb_online(struct smb_charger *chg, { int rc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->report_input_absent) { + val->intval = 0; + return rc; + } +#endif + rc = smblib_get_prop_usb_online(chg, val); if (!val->intval) goto exit; @@ -3531,6 +5889,25 @@ int smblib_get_prop_usb_voltage_now(struct smb_charger *chg, return ret; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static int smblib_get_usb_in_voltage_now(struct smb_charger *chg, + union power_supply_propval *val) +{ + int rc, ret = 0; + + if (chg->iio.usbin_v_chan) { + rc = iio_read_channel_processed(chg->iio.usbin_v_chan, + &val->intval); + if (rc < 0) + ret = -ENODATA; + } else { + ret = -ENODATA; + } + + return ret; +} +#endif + int smblib_get_prop_vph_voltage_now(struct smb_charger *chg, union power_supply_propval *val) { @@ -3684,6 +6061,10 @@ static int smblib_get_prop_ufp_mode(struct smb_charger *chg) { int rc; u8 stat; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + union power_supply_propval val = {0, }; + int usb_present = 0; +#endif rc = smblib_read(chg, TYPE_C_SNK_STATUS_REG, &stat); if (rc < 0) { @@ -3704,11 +6085,38 @@ static int smblib_get_prop_ufp_mode(struct smb_charger *chg) case SNK_DAM_500MA_BIT: case SNK_DAM_1500MA_BIT: case SNK_DAM_3000MA_BIT: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + return POWER_SUPPLY_TYPEC_SOURCE_DEFAULT; +#else return POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY; +#endif default: break; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* workaround for scp cable or similar A TO C cables */ + rc = smblib_read(chg, TYPE_C_SNK_DEBUG_ACC_STATUS_REG, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read TYPE_C_STATUS_1 rc=%d\n", rc); + return POWER_SUPPLY_TYPEC_NONE; + } + + rc = smblib_get_prop_usb_present(chg, &val); + if (rc < 0) + smblib_err(chg, "Couldn't get usb present rc = %d\n", rc); + else + usb_present = val.intval; + + if (chg->snk_debug_acc_detected && usb_present) { + return POWER_SUPPLY_TYPEC_SOURCE_DEFAULT; + } + if (stat & SNK_DEBUG_ACC_RPSTD_PRSTD_BIT && usb_present) { + chg->snk_debug_acc_detected = true; + return POWER_SUPPLY_TYPEC_SOURCE_DEFAULT; + } +#endif + return POWER_SUPPLY_TYPEC_NONE; } @@ -4212,6 +6620,7 @@ int smblib_get_prop_connector_health(struct smb_charger *chg) return POWER_SUPPLY_HEALTH_COOL; } +#ifndef CONFIG_MACH_XIAOMI_SM8250 static int get_rp_based_dcp_current(struct smb_charger *chg, int typec_mode) { int rp_ua; @@ -4229,6 +6638,7 @@ static int get_rp_based_dcp_current(struct smb_charger *chg, int typec_mode) return rp_ua; } +#endif /******************* * USB PSY SETTERS * @@ -4251,13 +6661,33 @@ int smblib_set_prop_pd_current_max(struct smb_charger *chg, return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define FLOAT_CHARGER_UA 1000000 +#define SUSPEND_CURRENT_UA 2000 +#endif static int smblib_handle_usb_current(struct smb_charger *chg, int usb_current) { +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int rc = 0, typec_mode; + bool is_float = false; +#else int rc = 0, rp_ua, typec_mode; +#endif union power_supply_propval val = {0, }; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT && (usb_current == SUSPEND_CURRENT_UA)) + is_float = true; + + if ((usb_current > 0 && usb_current < USBIN_500MA) || (usb_current == USBIN_900MA)) + usb_current = USBIN_500MA; +#endif + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) { +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (usb_current == -ETIMEDOUT || is_float) { +#else if (usb_current == -ETIMEDOUT) { if ((chg->float_cfg & FLOAT_OPTIONS_MASK) == FORCE_FLOAT_SDP_CFG_BIT) { @@ -4273,6 +6703,7 @@ static int smblib_handle_usb_current(struct smb_charger *chg, rc); return rc; } +#endif if (chg->connector_type == POWER_SUPPLY_CONNECTOR_TYPEC) { @@ -4281,10 +6712,16 @@ static int smblib_handle_usb_current(struct smb_charger *chg, * based of Rp. */ typec_mode = smblib_get_prop_typec_mode(chg); +#ifndef CONFIG_MACH_XIAOMI_SM8250 rp_ua = get_rp_based_dcp_current(chg, typec_mode); +#endif rc = vote(chg->usb_icl_votable, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + SW_ICL_MAX_VOTER, true, FLOAT_CHARGER_UA); +#else SW_ICL_MAX_VOTER, true, rp_ua); +#endif if (rc < 0) return rc; } else { @@ -4342,6 +6779,13 @@ int smblib_set_prop_sdp_current_max(struct smb_charger *chg, union power_supply_propval pval; int rc = 0; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->power_good_en) { + smblib_dbg(chg, PR_MISC, "power good on, don't set sdp icl\n"); + return rc; + } +#endif + if (!chg->pd_active) { rc = smblib_get_prop_usb_present(chg, &pval); if (rc < 0) { @@ -4521,6 +6965,27 @@ int smblib_set_prop_typec_power_role(struct smb_charger *chg, return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_set_prop_typec_boost_otg_disable(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc = 0; + + if (!chg->wireless_bq) + return 0; + + if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) + return 0; + + if (val->intval == 1 && !chg->power_good_en) { + pr_err("Type-C snk disconnect and no wireless charging, set bq_en q1 gpio to low now\n"); + smblib_dc_chg_q1_enable(chg, false); + } + + return rc; +} +#endif + int smblib_set_prop_typec_select_rp(struct smb_charger *chg, const union power_supply_propval *val) { @@ -4534,7 +6999,11 @@ int smblib_set_prop_typec_select_rp(struct smb_charger *chg, if (val->intval < TYPEC_SRC_RP_MAX_ELEMENTS) { rc = smblib_masked_write(chg, TYPE_C_CURRSRC_CFG_REG, TYPEC_SRC_RP_SEL_MASK, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + 0); +#else val->intval); +#endif if (rc < 0) smblib_err(chg, "Couldn't write to TYPE_C_CURRSRC_CFG rc=%d\n", rc); @@ -4616,6 +7085,15 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, smblib_apsd_enable(chg, !chg->pd_active); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (!chg->pd && chg->use_bq_pump) { + chg->pd = devm_usbpd_get_by_phandle(chg->dev, + "qcom,usbpd-phandle"); + if (IS_ERR_OR_NULL(chg->pd)) + pr_err("Failed to get pd handle %ld\n", + PTR_ERR(chg->pd)); + } +#endif update_sw_icl_max(chg, apsd->pst); if (chg->pd_active) { @@ -4633,6 +7111,15 @@ 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); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /*set the fcc to PD_UNVERIFED_CURRENT when pd is not verifed*/ + if (!chg->pd_verifed) { + rc = vote(chg->fcc_votable, PD_VERIFED_VOTER, + true, PD_UNVERIFED_CURRENT); + if (rc < 0) + smblib_err(chg, "Couldn't unvote PD_VERIFED_VOTER, rc=%d\n", rc); + } +#endif /* * For PPS, Charge Pump is preferred over parallel charger if * present. @@ -4646,6 +7133,17 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, dev_err(chg->dev, "Couldn't enable secondary charger rc=%d\n", rc); } + +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->pd_active == POWER_SUPPLY_PD_PPS_ACTIVE + && chg->six_pin_step_charge_enable) { + /* start six pin battery step charge monitor work */ + schedule_delayed_work(&chg->six_pin_batt_step_chg_work, + msecs_to_jiffies(STEP_CHG_DELAYED_START_MS)); + } + smblib_therm_charging(chg); +#endif + } else { vote(chg->usb_icl_votable, PD_VOTER, false, 0); vote(chg->limited_irq_disable_votable, CHARGER_TYPE_VOTER, @@ -4670,6 +7168,9 @@ int smblib_set_prop_pd_active(struct smb_charger *chg, } } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (!chg->fake_usb_insertion) +#endif smblib_usb_pd_adapter_allowance_override(chg, !!chg->pd_active ? FORCE_5V : FORCE_NULL); smblib_update_usb_type(chg); @@ -4912,6 +7413,16 @@ int smblib_get_charge_current(struct smb_charger *chg, typec_source_rd = smblib_get_prop_ufp_mode(chg); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* QC 3.5 adapter */ + if (chg->qc3p5_supported && + (apsd_result->pst == POWER_SUPPLY_TYPE_USB_HVDCP_3P5) && + (chg->qc3p5_power_limit_w == 40)) { + *total_current_ua = HVDCP3P5_40W_CURRENT_UA; + return 0; + } +#endif + /* QC 2.0/3.0 adapter */ if (apsd_result->bit & (QC_3P0_BIT | QC_2P0_BIT)) { *total_current_ua = HVDCP_CURRENT_UA; @@ -4997,6 +7508,21 @@ int smblib_set_prop_thermal_overheat(struct smb_charger *chg, return 0; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +/*************************************************** + * Enable OTG CHECK BY IRQ WHILE WIRELESS CHARGING * + ***************************************************/ +static void smblib_enable_otg_check_wl(struct smb_charger *chg, int enable){ + if(enable){ + smblib_masked_write(chg, TYPE_C_MODE_CFG_REG, EN_TRY_SNK_BIT, 0); + smblib_masked_write(chg, TYPE_C_EXIT_STATE_CFG_REG, BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT, BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT); + }else{ + smblib_masked_write(chg, TYPE_C_MODE_CFG_REG, EN_TRY_SNK_BIT, EN_TRY_SNK_BIT); + smblib_masked_write(chg, TYPE_C_EXIT_STATE_CFG_REG, BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT, 0); + } +} +#endif + /********************** * INTERRUPT HANDLERS * **********************/ @@ -5010,6 +7536,118 @@ irqreturn_t default_irq_handler(int irq, void *data) return IRQ_HANDLED; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define DELAY_BEFORE_OPEN_NCP_MS 50 +#define WIRELESS_INIT_ICL_UA 30000 +irqreturn_t dc_power_on_irq_handler(int irq, void *data) +{ + struct smb_irq_data *irq_data = data; + struct smb_charger *chg = irq_data->parent_data; + int rc; + u8 stat; + union power_supply_propval pval = {0, }; + bool dc_power_on; + + smblib_dbg(chg, PR_OEM, "IRQ: %s\n", irq_data->name); + + rc = smblib_read(chg, DCIN_BASE + INT_RT_STS_OFFSET, &stat); + if (rc < 0) { + smblib_err(chg, "Couldn't read DCIN_RT_STS rc=%d\n", rc); + return IRQ_HANDLED; + } + printk("dcin int rt status stat val is 0x%x, reverse_gpio_state;%d\n", stat, chg->reverse_gpio_state); + + /* return while in reverse charging or fw updating state */ + if (chg->wireless_bq) { + if (chg->reverse_gpio_state == REVERSE_GPIO_STATE_START + ||chg->reverse_gpio_state == REVERSE_GPIO_STATE_END) { + + /* if reverse gpio is end, set status to unset for normal operation */ + if (chg->reverse_gpio_state == REVERSE_GPIO_STATE_END) { + chg->reverse_gpio_state = REVERSE_GPIO_STATE_UNSET; + } + + /* ignore all the irq between REVERSE_GPIO_STATE_START and REVERSE_GPIO_STATE_END */ + return IRQ_HANDLED; + } + } + rc = smblib_get_prop_dc_present(chg, &pval); + if (rc < 0) { + pr_err("Couldn't get dc present rc=%d\n", rc); + return IRQ_HANDLED; + } + + dc_power_on = pval.intval; + + if (chg->wireless_bq) { + if (dc_power_on) { + /* when dc plug in, enable batt_temp irq wakeup */ + if (chg->irq_info[BAT_TEMP_IRQ].irq && !chg->batt_temp_irq_enabled) { + enable_irq_wake(chg->irq_info[BAT_TEMP_IRQ].irq); + chg->batt_temp_irq_enabled = true; + } + if (chg->power_good_en) { + smblib_enable_otg_check_wl(chg, true); + vote(chg->usb_icl_votable, WIRELESS_BY_USB_IN_VOTER, + true, WIRELESS_INIT_ICL_UA); + schedule_delayed_work(&chg->dc_power_work, msecs_to_jiffies(DELAY_BEFORE_OPEN_NCP_MS)); + smblib_set_wireless_present(chg, true); + if (chg->six_pin_step_charge_enable) + smblib_get_start_vbat_before_step_charge(chg); +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + smblib_check_batt_authentic(chg); +#endif + } else { + pr_err("dc_power_on without power good\n"); + } + vote(chg->awake_votable, DC_AWAKE_VOTER, true, 0); + } else { + /* when dc plug out, disable batt_temp irq wakeup */ + if (chg->irq_info[BAT_TEMP_IRQ].irq && chg->batt_temp_irq_enabled) { + disable_irq_wake(chg->irq_info[BAT_TEMP_IRQ].irq); + chg->batt_temp_irq_enabled = false; + } + /* when dc plug out, set bark timer back to default 16s */ + smblib_set_wdog_bark_timer(chg, BARK_TIMER_NORMAL); + if (chg->six_pin_step_charge_enable) { + chg->init_start_vbat_checked = false; + chg->trigger_taper_count = 0; + chg->index_vfloat = 0; + chg->health_not_good = false; + vote(chg->fv_votable, SIX_PIN_VFLOAT_VOTER, + false, 0); + vote(chg->fcc_votable, SIX_PIN_VFLOAT_VOTER, + false, 0); + vote(chg->usb_icl_votable, MAIN_ICL_MIN_VOTER, + false, 0); + } + chg->report_input_absent = false; + smblib_enable_otg_check_wl(chg, false); + smblib_set_wireless_present(chg, false); + cancel_delayed_work(&chg->dc_power_work); + if (is_ncp3902_pull_low(chg)) + smblib_ncp3902_enable(chg, true); + /* when dc plug out, set bark timer back to default 16s */ + smblib_set_wdog_bark_timer(chg, BARK_TIMER_NORMAL); + vote(chg->usb_icl_votable, WIRELESS_BY_USB_IN_VOTER, + true, USBIN_30MA); + vote(chg->usb_icl_votable, WIRELESS_BY_USB_IN_VOTER, + false, 0); + vote(chg->usb_icl_votable, MAIN_CHG_SUSPEND_VOTER, + false, 0); + vote(chg->awake_votable, DC_AWAKE_VOTER, false, 0); + } + } + + if (chg->dc_psy) + power_supply_changed(chg->dc_psy); + if (chg->wls_psy) + power_supply_changed(chg->wls_psy); + + return IRQ_HANDLED; +} +#endif + irqreturn_t smb_en_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; @@ -5068,6 +7706,12 @@ static void smblib_eval_chg_termination(struct smb_charger *chg, u8 batt_status) union power_supply_propval pval = {0, }; int rc = 0; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->ext_fg) + rc = smblib_get_prop_from_bms(chg, + POWER_SUPPLY_PROP_CAPACITY_RAW, &pval); + else +#endif rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_REAL_CAPACITY, &pval); if (rc < 0) { @@ -5075,6 +7719,10 @@ static void smblib_eval_chg_termination(struct smb_charger *chg, u8 batt_status) return; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->ext_fg) + pval.intval = pval.intval / 100; +#endif /* * Post charge termination, switch to BSM mode triggers the risk of * over charging as BATFET opening may take some time post the necessity @@ -5099,12 +7747,18 @@ static void smblib_eval_chg_termination(struct smb_charger *chg, u8 batt_status) } } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define WIRELESS_DELAY_WAKE_MS 20000 +#endif irqreturn_t chg_state_change_irq_handler(int irq, void *data) { struct smb_irq_data *irq_data = data; struct smb_charger *chg = irq_data->parent_data; u8 stat; int rc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int chg_en; +#endif smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name); @@ -5120,6 +7774,17 @@ irqreturn_t chg_state_change_irq_handler(int irq, void *data) if (chg->wa_flags & CHG_TERMINATION_WA) smblib_eval_chg_termination(chg, stat); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg_en = !(get_client_vote(chg->usb_icl_votable, MAIN_CHG_SUSPEND_VOTER) + == MAIN_CHG_SUSPEND_ICL); + + if (chg_en && (stat == TERMINATE_CHARGE) && (chg->power_good_en)) { + smblib_dbg(chg, PR_OEM, "full delay clear dc wake lock\n"); + schedule_delayed_work(&chg->wireless_full_delay_work, + msecs_to_jiffies(WIRELESS_DELAY_WAKE_MS)); + } +#endif + power_supply_changed(chg->batt_psy); return IRQ_HANDLED; } @@ -5239,6 +7904,9 @@ irqreturn_t usbin_uv_irq_handler(int irq, void *data) /* Workaround for non-QC2.0-compliant chargers follows */ if (!chg->qc2_unsupported_voltage && +#ifdef CONFIG_MACH_XIAOMI_SM8250 + !chg->qc2_unsupported && +#endif apsd->pst == POWER_SUPPLY_TYPE_USB_HVDCP) { rc = smblib_read(chg, QC_CHANGE_STATUS_REG, &stat); if (rc < 0) @@ -5284,6 +7952,21 @@ irqreturn_t usbin_uv_irq_handler(int irq, void *data) rc); smblib_rerun_apsd(chg); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + pr_info("qc2_unsupported charger detected\n"); + rc = smblib_force_vbus_voltage(chg, FORCE_5V_BIT); + if (rc < 0) + pr_err("Failed to force 5V\n"); + rc = smblib_usb_pd_adapter_allowance_override(chg, FORCE_5V); + if (rc < 0) + pr_err("Failed to set adapter allowance to 5V\n"); + rc = smblib_set_opt_switcher_freq(chg, chg->chg_freq.freq_5V); + if (rc < 0) + pr_err("Failed to set chg_freq.freq_5V\n"); + vote(chg->usb_icl_votable, QC2_UNSUPPORTED_VOTER, true, + QC2_UNSUPPORTED_UA); + chg->qc2_unsupported = true; +#endif } return IRQ_HANDLED; @@ -5438,6 +8121,189 @@ static void typec_partner_unregister(struct smb_charger *chg) mutex_unlock(&chg->typec_lock); } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#ifndef CONFIG_FUEL_GAUGE_BQ27Z561 +#define REDUCED_CURRENT 1000000 +#define REDUCED_CURRENT_LOW 500000 +#define FCC_LOW_FOR_ESR_THR 3000000 +static int smblib_get_effective_fcc_val(struct smb_charger *chg) +{ + int effective_fcc_val = 0; + + effective_fcc_val = get_effective_result(chg->fcc_votable); + return effective_fcc_val; +} + +static int check_reduce_fcc_condition(struct smb_charger *chg) +{ + union power_supply_propval val = {0, }; + int rc, ibat = 0; + int effective_fcc = 0; + + if (!chg->cp_psy) { + chg->cp_psy = power_supply_get_by_name("bq2597x-standalone"); + if (!chg->cp_psy) + pr_err("cp_psy not found\n"); + return 0; + } + + rc = power_supply_get_property(chg->cp_psy, + POWER_SUPPLY_PROP_CHARGING_ENABLED, &val); + if (rc < 0) { + pr_err("Error in getting cp charge enable, rc=%d\n", rc); + return 0; + } + chg->cp_charge_enabled = !!val.intval; + + rc = power_supply_get_property(chg->batt_psy, POWER_SUPPLY_PROP_STATUS, + &val); + if (rc < 0) { + pr_err("Error in getting charging status, rc=%d\n", rc); + return 0; + } + chg->charge_status = val.intval; + + rc = power_supply_get_property(chg->batt_psy, + POWER_SUPPLY_PROP_CHARGE_TYPE, &val); + if (rc < 0) { + pr_err("Error in getting charge type, rc=%d\n", rc); + return 0; + } + chg->charge_type = val.intval; + + rc = power_supply_get_property(chg->batt_psy, POWER_SUPPLY_PROP_HEALTH, + &val); + if (rc < 0) { + pr_err("Error in getting charging status, rc=%d\n", rc); + return 0; + } + chg->batt_health = val.intval; + + pr_info("cp_charge_enabled(%d), charge_status(%d), charge_type(%d), batt_health(%d)\n", + chg->cp_charge_enabled, chg->charge_status, chg->charge_type, chg->batt_health); + + /* should add battery health later */ + if (!chg->cp_charge_enabled || + (chg->charge_status != POWER_SUPPLY_STATUS_CHARGING) || + (chg->charge_type != POWER_SUPPLY_CHARGE_TYPE_FAST) || + (chg->batt_health != POWER_SUPPLY_HEALTH_GOOD)) + return 0; + + rc = smblib_get_prop_from_bms(chg, + POWER_SUPPLY_PROP_CURRENT_NOW, &val); + if (rc < 0) + return 0; + + ibat = val.intval/1000; + if (ibat > -450) { + pr_info("Skip CHG ESR, Fails IBAT ibat(%d)\n", ibat); + return 0; + } + + effective_fcc = smblib_get_effective_fcc_val(chg); + if (effective_fcc < 0) + return 0; + + return effective_fcc; +} + +/* In order to calculate battery esr */ +static void reduce_fcc_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + reduce_fcc_work.work); + int effective_fcc; + int esr_work_time = ESR_WORK_TIME_97S; + bool reduce_fcc = false; + + effective_fcc = check_reduce_fcc_condition(chg); + if (effective_fcc > 0) { + if (chg->esr_work_status == ESR_CHECK_FCC_NOLIMIT) { + if (effective_fcc >= FCC_LOW_FOR_ESR_THR) + effective_fcc -= REDUCED_CURRENT; + else + effective_fcc -= REDUCED_CURRENT_LOW; + chg->esr_work_status = ESR_CHECK_FCC_LIMITED; + reduce_fcc = true; + esr_work_time = ESR_WORK_TIME_2S; + } else { + chg->esr_work_status = ESR_CHECK_FCC_NOLIMIT; + reduce_fcc = false; + esr_work_time = ESR_WORK_TIME_97S; + } + } else { + chg->esr_work_status = ESR_CHECK_FCC_NOLIMIT; + reduce_fcc = false; + esr_work_time = ESR_WORK_TIME_97S; + pr_info("calculate esr condition not satisfy.\n"); + } + + vote(chg->fcc_votable, ESR_WORK_VOTER, reduce_fcc, effective_fcc); + schedule_delayed_work(&chg->reduce_fcc_work, + msecs_to_jiffies(esr_work_time)); +} +#endif + +static void smblib_cc_un_compliant_charge_work(struct work_struct *work) +{ + union power_supply_propval val = {0, }; + int rc, usb_present = 0; + + struct smb_charger *chg = container_of(work, struct smb_charger, + cc_un_compliant_charge_work.work); + + rc = smblib_get_prop_usb_present(chg, &val); + if (rc < 0) { + smblib_err(chg, "Couldn't get usb present rc = %d\n", rc); + return; + } + + usb_present = val.intval; + + /* + * workaround for typec audio/charger connector charge issue + * when firstly insert earphone then insert charger, it will trigger usbin_plugin irq, + * but not trigger typec_attach_detach irq. so need force set charger type and ICL here. + */ + if (usb_present && chg->typec_mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) { + chg->real_charger_type = POWER_SUPPLY_TYPE_USB_FLOAT; + chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_FLOAT; + if ((strcmp(get_effective_client(chg->usb_icl_votable), "OTG_VOTER") == 0) && + (get_effective_result(chg->usb_icl_votable) == 0)) + vote(chg->usb_icl_votable, OTG_VOTER, false, 0); + + if (get_client_vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER) != 500000) + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, 500000); + } + + /* + * if CC pin of C to A cable is not connected to the receptacle + * or CC pin is bad or short to VBUS or C to C cable CC line is float, + * enable hvdcp detect since pm8150b CC protection voltage + * threshold is very high (22V), our wire charging maxium charging + * vbus is below 13.2V. + */ + if (usb_present + && (chg->typec_mode == POWER_SUPPLY_TYPEC_NONE || + chg->typec_mode == POWER_SUPPLY_TYPEC_NON_COMPLIANT || + chg->snk_debug_acc_detected == true) + && (chg->cc_un_compliant_detected == false)) { + chg->cc_un_compliant_detected = true; + smblib_hvdcp_detect_enable(chg, true); + smblib_rerun_apsd_if_required(chg); + } +} + +static void smb_check_init_boot(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + check_init_boot.work); + first_boot_flag = true; + if (chg->usb_psy) + power_supply_changed(chg->usb_psy); +} +#endif + static void smblib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising) { int rc = 0; @@ -5472,9 +8338,24 @@ void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg) vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); if (vbus_rising) { +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* hold a wakeup source when charger is present */ + vote(chg->awake_votable, CHG_AWAKE_VOTER, true, 0); + /* when vbus present, enable batt_temp irq wakeup */ + if (chg->irq_info[BAT_TEMP_IRQ].irq && !chg->batt_temp_irq_enabled) { + enable_irq_wake(chg->irq_info[BAT_TEMP_IRQ].irq); + chg->batt_temp_irq_enabled = true; + } + if (chg->wireless_bq && !chg->power_good_en) + smblib_ncp3902_enable(chg, true); +#endif /* Remove FCC_STEPPER 1.5A init vote to allow FCC ramp up */ if (chg->fcc_stepper_enable) vote(chg->fcc_votable, FCC_STEPPER_VOTER, false, 0); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->six_pin_step_charge_enable) + smblib_get_start_vbat_before_step_charge(chg); +#endif } else { if (chg->wa_flags & BOOST_BACK_WA) { data = chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data; @@ -5489,10 +8370,30 @@ void smblib_usb_plugin_hard_reset_locked(struct smb_charger *chg) } } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* when vbus absent, disable batt_temp irq wakeup */ + if (chg->irq_info[BAT_TEMP_IRQ].irq && chg->batt_temp_irq_enabled) { + disable_irq_wake(chg->irq_info[BAT_TEMP_IRQ].irq); + chg->batt_temp_irq_enabled = false; + } + + cancel_delayed_work_sync(&chg->charger_type_recheck); + chg->hvdcp_recheck_status = false; + chg->recheck_charger = false; + chg->precheck_charger_type = POWER_SUPPLY_TYPE_UNKNOWN; + if (chg->cc_un_compliant_detected) { + smblib_hvdcp_detect_enable(chg, false); + chg->cc_un_compliant_detected = false; + } +#endif /* Force 1500mA FCC on USB removal if fcc stepper is enabled */ if (chg->fcc_stepper_enable) vote(chg->fcc_votable, FCC_STEPPER_VOTER, true, 1500000); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* clear chg_awake wakeup source when charger is absent */ + vote(chg->awake_votable, CHG_AWAKE_VOTER, false, 0); +#endif } power_supply_changed(chg->usb_psy); @@ -5505,7 +8406,9 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) { int rc; u8 stat; +#ifndef CONFIG_MACH_XIAOMI_SM8250 bool vbus_rising; +#endif struct smb_irq_data *data; struct storm_watch *wdata; @@ -5515,38 +8418,133 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) return; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); + smblib_set_opt_switcher_freq(chg, chg->vbus_rising ? chg->chg_freq.freq_5V : +#else vbus_rising = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); smblib_set_opt_switcher_freq(chg, vbus_rising ? chg->chg_freq.freq_5V : +#endif chg->chg_freq.freq_removal); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + schedule_work(&chg->plugin_check_time_work); +#endif +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->vbus_rising) { +#else if (vbus_rising) { +#endif +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* when vbus present, enable batt_temp irq wakeup */ + if (chg->irq_info[BAT_TEMP_IRQ].irq && !chg->batt_temp_irq_enabled) { + enable_irq_wake(chg->irq_info[BAT_TEMP_IRQ].irq); + chg->batt_temp_irq_enabled = true; + } + if (chg->wireless_bq) { + /* if vbus present, but power good is low, set it high */ + if (!chg->power_good_en) { + smblib_ncp3902_enable(chg, true); + } + } + if (smblib_get_prop_dfp_mode(chg) != POWER_SUPPLY_TYPEC_NONE + && smblib_get_prop_dfp_mode(chg) + != POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) { + chg->fake_usb_insertion = true; + return; + } + + /* hold a wakeup source when charger is present */ + vote(chg->awake_votable, CHG_AWAKE_VOTER, true, 0); +#endif cancel_delayed_work_sync(&chg->pr_swap_detach_work); vote(chg->awake_votable, DETACH_DETECT_VOTER, false, 0); rc = smblib_request_dpdm(chg, true); if (rc < 0) smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc); +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + smblib_check_batt_authentic(chg); +#endif + /* Enable SW Thermal regulation */ rc = smblib_set_sw_thermal_regulation(chg, true); if (rc < 0) smblib_err(chg, "Couldn't start SW thermal regulation WA, rc=%d\n", rc); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* Enable SW conn therm Regulation */ + if (chg->support_conn_therm) { + rc = smblib_set_sw_conn_therm_regulation(chg, true); + if (rc < 0) + smblib_err(chg, "Couldn't start SW conn therm rc=%d\n", rc); + } +#endif /* Remove FCC_STEPPER 1.5A init vote to allow FCC ramp up */ if (chg->fcc_stepper_enable) vote(chg->fcc_votable, FCC_STEPPER_VOTER, false, 0); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->six_pin_step_charge_enable) + smblib_get_start_vbat_before_step_charge(chg); +#endif + /* Schedule work to enable parallel charger */ vote(chg->awake_votable, PL_DELAY_VOTER, true, 0); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (!first_boot_flag) + schedule_delayed_work(&chg->check_init_boot, msecs_to_jiffies(45000)); +#endif schedule_delayed_work(&chg->pl_enable_work, msecs_to_jiffies(PL_DELAY_MS)); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + schedule_delayed_work(&chg->charger_type_recheck, + msecs_to_jiffies(CHARGER_RECHECK_DELAY_MS)); + schedule_delayed_work(&chg->cc_un_compliant_charge_work, + msecs_to_jiffies(CC_UN_COMPLIANT_START_DELAY_MS)); + if (chg->ext_fg) + schedule_delayed_work(&chg->after_ffc_chg_dis_work, + msecs_to_jiffies(FFC_DISABLE_CHG_RECHECK_DELAY_10S)); +#endif } else { +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* when vbus absent, disable batt_temp irq wakeup */ + if (chg->irq_info[BAT_TEMP_IRQ].irq && chg->batt_temp_irq_enabled) { + disable_irq_wake(chg->irq_info[BAT_TEMP_IRQ].irq); + chg->batt_temp_irq_enabled = false; + } + if (chg->fake_usb_insertion) { + chg->fake_usb_insertion = false; + return; + } + chg->hvdcp_recheck_status = false; + cancel_delayed_work_sync(&chg->charger_type_recheck); + flush_delayed_work(&chg->after_ffc_chg_dis_work); + cancel_delayed_work(&chg->after_ffc_chg_en_work); + if (chg->cc_un_compliant_detected) { + smblib_hvdcp_detect_enable(chg, false); + chg->cc_un_compliant_detected = false; + } +#ifndef CONFIG_FUEL_GAUGE_BQ27Z561 + cancel_delayed_work_sync(&chg->reduce_fcc_work); + vote(chg->fcc_votable, ESR_WORK_VOTER, false, 0); +#endif +#endif /* Disable SW Thermal Regulation */ rc = smblib_set_sw_thermal_regulation(chg, false); if (rc < 0) smblib_err(chg, "Couldn't stop SW thermal regulation WA, rc=%d\n", rc); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* Disable SW conn therm Regulation */ + if (chg->support_conn_therm){ + rc = smblib_set_sw_conn_therm_regulation(chg, false); + if (rc < 0) + smblib_err(chg, "Couldn't start SW conn therm rc=%d\n", rc); + } +#endif if (chg->wa_flags & BOOST_BACK_WA) { data = chg->irq_info[SWITCHER_POWER_OK_IRQ].irq_data; if (data) { @@ -5596,18 +8594,41 @@ void smblib_usb_plugin_locked(struct smb_charger *chg) if (rc < 0) smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->qc2_unsupported) { + chg->qc2_unsupported = false; + rc = smblib_usb_pd_adapter_allowance_override(chg, FORCE_NULL); + } + chg->recheck_charger = false; + chg->precheck_charger_type = POWER_SUPPLY_TYPE_UNKNOWN; + /* clear chg_awake wakeup source when charger is absent */ + vote(chg->awake_votable, CHG_AWAKE_VOTER, false, 0); +#else smblib_update_usb_type(chg); +#endif } if (chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB) +#ifdef CONFIG_MACH_XIAOMI_SM8250 + smblib_micro_usb_plugin(chg, chg->vbus_rising); +#else smblib_micro_usb_plugin(chg, vbus_rising); +#endif vote(chg->temp_change_irq_disable_votable, DEFAULT_VOTER, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + !chg->vbus_rising, 0); +#else !vbus_rising, 0); +#endif power_supply_changed(chg->usb_psy); smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n", +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->vbus_rising ? "attached" : "detached"); +#else vbus_rising ? "attached" : "detached"); +#endif } irqreturn_t usb_plugin_irq_handler(int irq, void *data) @@ -5637,7 +8658,350 @@ static void smblib_handle_sdp_enumeration_done(struct smb_charger *chg, rising ? "rising" : "falling"); } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static bool qc3p5_vbus_timeout_check(struct smb_charger *chg, + int timeout_ms, int vbus_lo_bound, + int vbus_hi_bound, int *vbus_uv) +{ + union power_supply_propval pval; + ktime_t start_kt, delta_kt; + int rc = 0; + + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't get VBUS voltage rc=%d\n", rc); + return false; + } + + start_kt = ktime_get_boottime(); + delta_kt = ktime_sub(ktime_get_boottime(), start_kt); + while (((pval.intval <= vbus_lo_bound) || (pval.intval >= vbus_hi_bound)) + && (ktime_to_ms(delta_kt) < timeout_ms)) { + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval); + if (rc < 0) { + smblib_err(chg, "Couldn't get VBUS voltage rc=%d\n", rc); + return false; + } + delta_kt = ktime_sub(ktime_get_boottime(), start_kt); + } + + if ((pval.intval <= vbus_lo_bound) + || (pval.intval >= vbus_hi_bound) + || (ktime_to_ms(delta_kt) > timeout_ms)) { + smblib_err(chg, "QC3.5: VBUS failed to settle\n"); + return false; + } + + *vbus_uv = (int)pval.intval; + smblib_err(chg, "QC3.5: VBUS successfully settled\n"); + + return true; +} + +#define QC3P5_T_TA_DETECTION_TIMEOUT_PMIC_MS 200 +#define QC3P5_T_TA_CAP_TIMEOUT_PMIC_MS 250 +#define VBUS_5P5_V_UV 5500000 +#define VBUS_6P4_V_UV 6400000 +#define VBUS_6P65_V_UV 6650000 +#define VBUS_7P35_V_UV 7350000 +#define VBUS_7P6_V_UV 7600000 +#define VBUS_8P4_V_UV 8400000 +/*Use 8.55V and 9.8V to account for +10% and -5% spec on 9V for QC2/3 */ +#define VBUS_8P55_V_UV 8550000 +#define VBUS_9P8_V_UV 9800000 + +static int qc3p5_authenticate(struct smb_charger *chg) +{ + int i, vbus_uv, rc = 0; + + chg->qc3p5_authenticated = false; + chg->qc3p5_authentication_started = true; + chg->qc3p5_power_limit_w = 18;//Default to lowest power limit of 18W + + /* Set ICL to 500mA during QC3.5 Authentication */ + vote(chg->usb_icl_votable, QC3P5_VOTER, true, USBIN_500MA); + + /* + * * Fall back to QC3.0 Mode if VBUS doesn't settle between + * * 5.5V and 6.4V in 200ms + * */ + if (!qc3p5_vbus_timeout_check(chg, QC3P5_T_TA_DETECTION_TIMEOUT_PMIC_MS, + VBUS_5P5_V_UV, VBUS_6P4_V_UV, &vbus_uv)) { + smblib_err(chg, "VBUS doesn't reach 6V vbus=%d\n", vbus_uv); + return -ENODEV; + } + + smblib_err(chg, "QC3P5 AUTH: After QC3.0 Auth VBUS = %d\n", vbus_uv); + + /* Issue +-+-+- to request SRC CAP */ + for (i = 0; i < 3; i++) { + rc = smblib_dp_pulse(chg); + if (rc < 0) { + smblib_err(chg, "Couldn't issue D+ pulse rc=%d\n", rc); + return rc; + } + usleep_range(5000, 5010); + + rc = smblib_dm_pulse(chg); + if (rc < 0) { + smblib_err(chg, "Couldn't issue D- pulse rc=%d\n", rc); + return rc; + } + usleep_range(5000, 5010); + } + + /* Return to QC3.0 Mode if VBUS doesn't reach 7V in 200ms 50ms buffer */ + if (!qc3p5_vbus_timeout_check(chg, QC3P5_T_TA_CAP_TIMEOUT_PMIC_MS, + VBUS_6P65_V_UV, VBUS_9P8_V_UV, &vbus_uv)) { + smblib_err(chg, "VBUS doesn't reach 7V vbus=%d\n", vbus_uv); + return -ENODEV; + } + smblib_err(chg, "QC3P5 AUTH: SRC_CAP receieved\n"); + + /* Issue ++-- to confirm transition to QC3.5 */ + rc = smblib_dp_pulse(chg); + if (rc < 0) { + smblib_err(chg, "Couldn't issue D+ pulse rc=%d\n", rc); + return rc; + } + usleep_range(5000, 5010); + + rc = smblib_dp_pulse(chg); + if (rc < 0) { + smblib_err(chg, "Couldn't issue D+ pulse rc=%d\n", rc); + return rc; + } + usleep_range(5000, 5010); + + rc = smblib_dm_pulse(chg); + if (rc < 0) { + smblib_err(chg, "Couldn't issue D- pulse rc=%d\n", rc); + return rc; + } + usleep_range(5000, 5010); + + rc = smblib_dm_pulse(chg); + if (rc < 0) { + smblib_err(chg, "Couldn't issue D- pulse rc=%d\n", rc); + return rc; + } + + /* SRC CAP 7V for 18W */ + if ((vbus_uv >= VBUS_6P65_V_UV) && (vbus_uv <= VBUS_7P35_V_UV)) { + chg->qc3p5_power_limit_w = 18; + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, QC3P5_CHARGER_ICL); + /* SRC CAP 8V for 27W */ + } else if ((vbus_uv >= VBUS_7P6_V_UV) && (vbus_uv <= VBUS_8P4_V_UV)) { + chg->qc3p5_power_limit_w = 27; + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, QC3P5_CHARGER_ICL); + /* SRC CAP 9V for 40W */ + } else if ((vbus_uv >= VBUS_8P55_V_UV) && (vbus_uv <= VBUS_9P8_V_UV)) { + chg->qc3p5_power_limit_w = 40; + // QC3.5 40W adapter's icl limited to 4A + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, HVDCP3P5_40W_CURRENT_UA); + } else { + smblib_err(chg, "not supported SRC CAP, vbus=%d\n", vbus_uv); + return -ENODEV; + } + + chg->qc3p5_authenticated = true; + vote(chg->fcc_votable, FCC_MAX_QC3P5_VOTER, true, HVDCP3P5_40W_CURRENT_UA); + + if (chg->support_ffc && !smblib_get_fastcharge_mode(chg)) + smblib_set_fastcharge_mode(chg, true); + + smblib_err(chg, "QC3P5 AUTH: QC3.5 Authenticated\n"); + smblib_err(chg, "QC3P5 AUTH: Power Limit = %d\n", + chg->qc3p5_power_limit_w); + + return rc; +} +#endif + #define APSD_EXTENDED_TIMEOUT_MS 400 + +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static int smblib_hvdcp3_raise_fsw(struct smb_charger *chg, int pulse_count) +{ + if (pulse_count < QC3_PULSES_FOR_6V) + smblib_set_opt_switcher_freq(chg, + chg->chg_freq.freq_5V); + else if (pulse_count < QC3_PULSES_FOR_9V) + smblib_set_opt_switcher_freq(chg, + chg->chg_freq.freq_6V_8V); + else if (pulse_count < QC3_PULSES_FOR_12V) + smblib_set_opt_switcher_freq(chg, + chg->chg_freq.freq_9V); + else + smblib_set_opt_switcher_freq(chg, + chg->chg_freq.freq_12V); + return 0; +} + +static void smblib_raise_qc3_vbus_work(struct work_struct *work) +{ + union power_supply_propval val = {0, }; + int i, usb_present = 0, vbus_now = 0; + int vol_qc_ab_thr = 0; + int rc; + struct smb_charger *chg = container_of(work, struct smb_charger, + raise_qc3_vbus_work.work); + + rc = smblib_get_prop_usb_present(chg, &val); + if (rc < 0) { + smblib_err(chg, "Couldn't get usb present rc = %d\n", rc); + return; + } + + usb_present = val.intval; + if (usb_present) { + chg->raise_vbus_to_detect = true; + if (chg->no_raise_vbus_status == true) { + smblib_hvdcp3_raise_fsw(chg, MAX_PULSE); + chg->is_qc_class_a = true; + vote(chg->fcc_votable, + CLASSA_QC_FCC_VOTER, true, QC_CLASS_A_CURRENT_UA); + goto skip; + } + + for (i = 0; i < MAX_PULSE; i++) { + msleep(40); + smblib_hvdcp3_raise_fsw(chg, i); + rc = smblib_dp_pulse(chg); + } + chg->after_raise_vbus_time = ktime_get(); + + msleep(200); + rc = smblib_get_prop_usb_present(chg, &val); + if (rc < 0) { + smblib_err(chg, "Couldn't get usb present rc = %d\n", rc); + return; + } + + usb_present = val.intval; + pr_info("usb_present is %d\n", usb_present); + if (!usb_present) { + chg->raise_vbus_to_detect = false; + rc = smblib_force_vbus_voltage(chg, FORCE_5V_BIT); + if (rc < 0) + pr_err("Failed to force 5V\n"); + return; + } + + rc = smblib_get_usb_in_voltage_now(chg, &val); + if (rc < 0) + pr_err("Couldn't get usb voltage rc=%d\n", rc); + vbus_now = val.intval; + pr_info("vbus_now is %d\n", vbus_now); + if (chg->snk_debug_acc_detected && usb_present) + vol_qc_ab_thr = VOL_THR_FOR_QC_CLASS_AB + + COMP_FOR_LOW_RESISTANCE_CABLE; + else + vol_qc_ab_thr = VOL_THR_FOR_QC_CLASS_AB; + if (vbus_now <= vol_qc_ab_thr) { + pr_info("qc_class_a charger is detected\n"); + chg->is_qc_class_a = true; + vote(chg->fcc_votable, + CLASSA_QC_FCC_VOTER, true, QC_CLASS_A_CURRENT_UA); + } else { + pr_info("qc_class_b charger is detected\n"); + chg->is_qc_class_b = true; + if (chg->usb_psy) + power_supply_changed(chg->usb_psy); + } +skip: + rc = smblib_force_vbus_voltage(chg, FORCE_5V_BIT); + if (rc < 0) + pr_err("Failed to force 5V\n"); + if (chg->is_qc_class_a) + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + HVDCP_CLASS_A_MAX_UA); + else { + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + HVDCP_CURRENT_UA); + + if (!is_client_vote_enabled(chg->fcc_votable, + PD_VERIFED_VOTER)) + vote(chg->fcc_votable, + PD_VERIFED_VOTER, false, 0); + + if (chg->six_pin_step_charge_enable) { + /* start six pin step charge monitor work */ + schedule_delayed_work(&chg->six_pin_batt_step_chg_work, + msecs_to_jiffies(STEP_CHG_DELAYED_START_MS)); + } + } + /* select charge pump as second charger */ + if (chg->sec_cp_present){ + rc = smblib_select_sec_charger(chg, POWER_SUPPLY_CHARGER_SEC_CP, + POWER_SUPPLY_CP_HVDCP3, false); + if (rc < 0) + dev_err(chg->dev, + "Couldn't enable secondary chargers rc=%d\n", rc); + } + + smblib_therm_charging(chg); + chg->raise_vbus_to_detect = false; + } +} + +struct quick_charge adapter_cap[11] = { + { POWER_SUPPLY_TYPE_USB, QUICK_CHARGE_NORMAL }, + { POWER_SUPPLY_TYPE_USB_DCP, QUICK_CHARGE_NORMAL }, + { POWER_SUPPLY_TYPE_USB_CDP, QUICK_CHARGE_NORMAL }, + { POWER_SUPPLY_TYPE_USB_ACA, QUICK_CHARGE_NORMAL }, + { POWER_SUPPLY_TYPE_USB_FLOAT, QUICK_CHARGE_NORMAL }, + { POWER_SUPPLY_TYPE_USB_PD, QUICK_CHARGE_FAST }, + { POWER_SUPPLY_TYPE_USB_HVDCP, QUICK_CHARGE_FAST }, + { POWER_SUPPLY_TYPE_USB_HVDCP_3, QUICK_CHARGE_FAST }, + { POWER_SUPPLY_TYPE_USB_HVDCP_3P5, QUICK_CHARGE_FAST }, + { POWER_SUPPLY_TYPE_WIRELESS, QUICK_CHARGE_FAST }, + {0, 0}, +}; + +int smblib_get_quick_charge_type(struct smb_charger *chg) +{ + int i = 0, rc; + union power_supply_propval pval = {0, }; + + if (!chg) { + dev_err(chg->dev, "get quick charge type faied\n"); + return -EINVAL; + } + + rc = smblib_get_prop_batt_health(chg, &pval); + if (rc < 0) + smblib_err(chg, "Couldn't get batt health rc=%d\n", rc); + + if ((pval.intval == POWER_SUPPLY_HEALTH_COLD) || (pval.intval == POWER_SUPPLY_HEALTH_OVERHEAT)) { + pr_info("battery temp is under 0 or above 58\n"); + return 0; + } + + if ((chg->real_charger_type == POWER_SUPPLY_TYPE_USB_PD) && chg->pd_verifed) { + return QUICK_CHARGE_TURBE; + } + + if (chg->is_qc_class_b || chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3P5) + return QUICK_CHARGE_FLASH; + + if ((chg->real_charger_type == POWER_SUPPLY_TYPE_USB_DCP) && + (chg->hvdcp_recheck_status || chg->fake_plug_out == true)) + return QUICK_CHARGE_FAST; + + while (adapter_cap[i].adap_type != 0) { + if (chg->real_charger_type == adapter_cap[i].adap_type) { + return adapter_cap[i].adap_cap; + } + i++; + } + + return 0; +} +#endif + /* triggers when HVDCP 3.0 authentication has finished */ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg, bool rising) @@ -5648,15 +9012,37 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg, if (!rising) return; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->qc3p5_supported) { + /* Run QC3P5 Authentication */ + if (!chg->qc3p5_authentication_started) { + rc = qc3p5_authenticate(chg); + if (rc < 0) { + chg->qc3p5_authenticated = false; + dev_err(chg->dev, "QC3.5 Authentication Failed, rc=%d\n", rc); + } + chg->qc3p5_auth_complete = true; + /* Release 500mA QC3.5 Authentication vote */ + vote(chg->usb_icl_votable, QC3P5_VOTER, false, 0); + } + smblib_update_usb_type(chg); + } +#endif + if (chg->mode == PARALLEL_MASTER) vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, true, 0); /* the APSD done handler will set the USB supply type */ apsd_result = smblib_get_apsd_result(chg); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if ((apsd_result->bit & QC_3P0_BIT) && chg->sec_cp_present) { + if (!chg->qc_class_ab) { +#else if (apsd_result->bit & QC_3P0_BIT) { /* for QC3, switch to CP if present */ if (chg->sec_cp_present) { +#endif rc = smblib_select_sec_charger(chg, POWER_SUPPLY_CHARGER_SEC_CP, POWER_SUPPLY_CP_HVDCP3, false); @@ -5664,8 +9050,19 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg, dev_err(chg->dev, "Couldn't enable secondary chargers rc=%d\n", rc); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + } else { + if (!chg->detect_low_power_qc3_charger && + (!chg->qc3p5_supported || !chg->qc3p5_authenticated)) { + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + HVDCP_START_CURRENT_UA); + schedule_delayed_work(&chg->raise_qc3_vbus_work, 0); + chg->detect_low_power_qc3_charger = true; + } +#endif } +#ifndef CONFIG_MACH_XIAOMI_SM8250 /* QC3.5 detection timeout */ if (!chg->apsd_ext_timeout && !timer_pending(&chg->apsd_timer)) { @@ -5677,6 +9074,24 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg, msecs_to_jiffies(APSD_EXTENDED_TIMEOUT_MS) + jiffies); } +#else + } else if ((apsd_result->bit & QC_3P0_BIT) && chg->use_bq_pump) { + if (!chg->detect_low_power_qc3_charger) { + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + HVDCP_START_CURRENT_UA); + schedule_delayed_work(&chg->raise_qc3_vbus_work, 0); + chg->detect_low_power_qc3_charger = true; + } + } else if (apsd_result->bit & QC_2P0_BIT) { + if (get_client_vote(chg->usb_icl_votable, USB_PSY_VOTER) == 500000) + vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); + pr_info("force 9V for QC2 charger\n"); + rc = smblib_force_vbus_voltage(chg, FORCE_9V_BIT); + if (rc < 0) + pr_err("Failed to force 9V\n"); + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + HVDCP2_CURRENT_UA); +#endif } smblib_dbg(chg, PR_INTERRUPT, "IRQ: hvdcp-3p0-auth-done rising; %s detected\n", @@ -5701,8 +9116,23 @@ 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 CONFIG_MACH_XIAOMI_SM8250 + if (!chg->raise_vbus_to_detect + && chg->real_charger_type != POWER_SUPPLY_TYPE_USB_HVDCP_3P5) { + if (chg->is_qc_class_a) + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + HVDCP_CLASS_A_MAX_UA); + else if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP) + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + HVDCP2_CURRENT_UA); + else + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + HVDCP_CURRENT_UA); + } +#else 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, @@ -5722,10 +9152,46 @@ static void smblib_handle_hvdcp_detect_done(struct smb_charger *chg, rising ? "rising" : "falling"); } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static bool smblib_handle_wireless_by_usbin_charge(struct smb_charger *chg) +{ + union power_supply_propval pval = {0, }; + union power_supply_propval val = {0, }; + int typec_mode; + int dc_power_on = 0; + + if (!chg->wireless_bq) + return false; + + smblib_get_prop_usb_present(chg, &val); + smblib_get_prop_dc_present(chg, &pval); + dc_power_on = pval.intval; + + typec_mode = smblib_get_prop_typec_mode(chg); + if ((typec_mode >= POWER_SUPPLY_TYPEC_NONE) + && (val.intval == 1) && dc_power_on){ + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0); + return true; + } + return false; +} +#endif + static void update_sw_icl_max(struct smb_charger *chg, int pst) { +#ifdef CONFIG_MACH_XIAOMI_SM8250 + union power_supply_propval val = {0, }; +#else int typec_mode; int rp_ua; +#endif + +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->power_good_en) { + smblib_dbg(chg, PR_MISC, "power good on, no update icl\n"); + return; + } +#endif /* while PD is active it should have complete ICL control */ if (chg->pd_active) @@ -5740,9 +9206,13 @@ static void update_sw_icl_max(struct smb_charger *chg, int pst) * HVDCP 2/3, handled separately */ if (pst == POWER_SUPPLY_TYPE_USB_HVDCP +#ifdef CONFIG_MACH_XIAOMI_SM8250 + || pst == POWER_SUPPLY_TYPE_USB_HVDCP_3P5 +#endif || pst == POWER_SUPPLY_TYPE_USB_HVDCP_3) return; +#ifndef CONFIG_MACH_XIAOMI_SM8250 /* TypeC rp med or high, use rp value */ typec_mode = smblib_get_prop_typec_mode(chg); if (typec_rp_med_high(chg, typec_mode)) { @@ -5750,47 +9220,96 @@ static void update_sw_icl_max(struct smb_charger *chg, int pst) vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, rp_ua); return; } +#endif /* rp-std or legacy, USB BC 1.2 */ switch (pst) { case POWER_SUPPLY_TYPE_USB: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + smblib_get_prop_usb_present(chg, &val); +#endif /* * USB_PSY will vote to increase the current to 500/900mA once * enumeration is done. */ if (!is_client_vote_enabled(chg->usb_icl_votable, USB_PSY_VOTER)) { +#ifdef CONFIG_MACH_XIAOMI_SM8250 + vote(chg->usb_icl_votable, USB_PSY_VOTER, true, SDP_CURRENT_UA); + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0); + } else if ((chg->typec_mode == POWER_SUPPLY_TYPEC_NONE) + && (val.intval == 1)) + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, SDP_CURRENT_UA); + else + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0); +#else /* if flash is active force 500mA */ vote(chg->usb_icl_votable, USB_PSY_VOTER, true, is_flash_active(chg) ? SDP_CURRENT_UA : SDP_100_MA); } vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0); +#endif break; case POWER_SUPPLY_TYPE_USB_CDP: vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, CDP_CURRENT_UA); break; case POWER_SUPPLY_TYPE_USB_DCP: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, DCP_CURRENT_UA); +#else rp_ua = get_rp_based_dcp_current(chg, typec_mode); vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, rp_ua); +#endif break; case POWER_SUPPLY_TYPE_USB_FLOAT: /* * limit ICL to 100mA, the USB driver will enumerate to check * if this is a SDP and appropriately set the current */ +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (smblib_handle_wireless_by_usbin_charge(chg)) + break; + + if (!chg->recheck_charger) +#endif vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, SDP_100_MA); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + else + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + FLOAT_CHARGER_UA); +#endif + break; case POWER_SUPPLY_TYPE_UNKNOWN: default: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (smblib_handle_wireless_by_usbin_charge(chg)) + break; +#endif + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, SDP_100_MA); break; } } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static void determine_thermal_current(struct smb_charger *chg) +{ + if (chg->system_temp_level > 0 + && chg->system_temp_level < (chg->thermal_levels - 1)) { + /* + * consider thermal limit only when it is active and not at + * the highest level + */ + smblib_therm_charging(chg); + } +} +#endif + static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) { const struct apsd_result *apsd_result; @@ -5816,6 +9335,9 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising) break; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + determine_thermal_current(chg); +#endif smblib_dbg(chg, PR_INTERRUPT, "IRQ: apsd-done rising; %s detected\n", apsd_result->name); } @@ -5827,6 +9349,11 @@ irqreturn_t usb_source_change_irq_handler(int irq, void *data) int rc = 0; u8 stat; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->fake_usb_insertion) + return IRQ_HANDLED; +#endif + /* PD session is ongoing, ignore BC1.2 and QC detection */ if (chg->pd_active) return IRQ_HANDLED; @@ -5985,6 +9512,17 @@ static void typec_sink_insertion(struct smb_charger *chg) { int rc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* always close q1 to prevent reverse current */ + if (chg->wireless_bq) { + smblib_dc_chg_q1_enable(chg, true); + smblib_set_wireless_otg_state(chg, true); + } + /* do not vote while wireless attached to allow charging via usb */ + if (chg->wireless_bq && !chg->power_good_en) + vote(chg->usb_icl_votable, OTG_VOTER, true, 0); +#endif + typec_src_fault_condition_cfg(chg, true); rc = smblib_set_charge_param(chg, &chg->param.freq_switcher, chg->chg_freq.freq_above_otg_threshold); @@ -6019,12 +9557,28 @@ static void typec_src_insertion(struct smb_charger *chg) } chg->typec_legacy = stat & TYPEC_LEGACY_CABLE_STATUS_BIT; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /*reset typec_legacy to detect PD when power_good_en online*/ + if (chg->power_good_en) + chg->typec_legacy = false; +#endif chg->ok_to_pd = (!(chg->typec_legacy || chg->pd_disabled) || chg->early_usb_attach) && !chg->pd_not_supported; /* allow apsd proceed to detect QC2/3 */ if (!chg->ok_to_pd) smblib_hvdcp_detect_try_enable(chg, true); + +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* rerun apsd while insert usb in wireless charging */ + if (chg->power_good_en) { + rc = smblib_request_dpdm(chg, true); + if (rc < 0) + smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc); + else + smblib_rerun_apsd(chg); + } +#endif } static void typec_ra_ra_insertion(struct smb_charger *chg) @@ -6177,6 +9731,18 @@ static void typec_sink_removal(struct smb_charger *chg) { int rc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* if wireless charging deatched, open q1 for wired charging */ + if (chg->wireless_bq && !chg->power_good_en) { + smblib_dc_chg_q1_enable(chg, false); + ncp3902_pass_enable(chg, false); + } + + if (chg->wireless_bq ) + smblib_set_wireless_otg_state(chg, false); + vote(chg->usb_icl_votable, OTG_VOTER, false, 0); +#endif + typec_src_fault_condition_cfg(chg, false); rc = smblib_set_charge_param(chg, &chg->param.freq_switcher, chg->chg_freq.freq_removal); @@ -6207,9 +9773,21 @@ static void typec_src_removal(struct smb_charger *chg) dev_err(chg->dev, "Couldn't disable secondary charger rc=%d\n", rc); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* Reset QC3.5 Flag and power limit*/ + chg->qc3p5_authenticated = false; + chg->qc3p5_auth_complete = false; + chg->qc3p5_authentication_started = false; + chg->qc3p5_dp_tune_rapidly = false; + chg->qc3p5_power_limit_w = 18; +#endif + chg->qc3p5_detected = false; typec_src_fault_condition_cfg(chg, false); smblib_hvdcp_detect_try_enable(chg, false); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->fake_plug_out == false) +#endif smblib_update_usb_type(chg); if (chg->wa_flags & BOOST_BACK_WA) { @@ -6224,6 +9802,10 @@ static void typec_src_removal(struct smb_charger *chg) } cancel_delayed_work_sync(&chg->pl_enable_work); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + cancel_delayed_work_sync(&chg->raise_qc3_vbus_work); + cancel_delayed_work_sync(&chg->check_init_boot); +#endif /* reset input current limit voters */ vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, @@ -6236,7 +9818,14 @@ static void typec_src_removal(struct smb_charger *chg) vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER, false, 0); vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, false, 0); vote(chg->usb_icl_votable, THERMAL_THROTTLE_VOTER, false, 0); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + vote(chg->usb_icl_votable, OTG_VOTER, false, 0); +#endif vote(chg->usb_icl_votable, LPD_VOTER, false, 0); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + vote(chg->usb_icl_votable, QC2_UNSUPPORTED_VOTER, false, 0); + vote(chg->usb_icl_votable, QC3P5_VOTER, false, 0); +#endif /* reset usb irq voters */ vote(chg->limited_irq_disable_votable, CHARGER_TYPE_VOTER, @@ -6259,6 +9848,16 @@ static void typec_src_removal(struct smb_charger *chg) vote(chg->cp_disable_votable, SW_THERM_REGULATION_VOTER, false, 0); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + vote(chg->fcc_votable, CLASSA_QC_FCC_VOTER, false, 0); + vote(chg->fcc_votable, PD_VERIFED_VOTER, false, 0); + vote(chg->usb_icl_votable, QC_A_CP_ICL_MAX_VOTER, false, 0); + vote(chg->usb_icl_votable, MAIN_CHG_SUSPEND_VOTER, false, 0); + vote(chg->usb_icl_votable, WIRELESS_BY_USB_IN_VOTER, false, 0); + vote(chg->chg_disable_votable, AFTER_FFC_VOTER, false, 0); + /* clear chg_awake wakeup source when typec removal */ + vote(chg->awake_votable, CHG_AWAKE_VOTER, false, 0); +#endif /* reset USBOV votes and cancel work */ cancel_delayed_work_sync(&chg->usbov_dbc_work); vote(chg->awake_votable, USBOV_DBC_VOTER, false, 0); @@ -6271,14 +9870,34 @@ static void typec_src_removal(struct smb_charger *chg) chg->usbin_forced_max_uv = 0; chg->chg_param.forced_main_fcc = 0; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->six_pin_step_charge_enable) { + chg->init_start_vbat_checked = false; + chg->trigger_taper_count = 0; + chg->index_vfloat = 0; + chg->health_not_good = false; + chg->flag_second_ffc_term_current = false; + vote(chg->fv_votable, SIX_PIN_VFLOAT_VOTER, false, 0); + vote(chg->fcc_votable, SIX_PIN_VFLOAT_VOTER, false, 0); + vote(chg->usb_icl_votable, MAIN_ICL_MIN_VOTER, false, 0); + } +#endif + /* Reset all CC mode votes */ vote(chg->fcc_main_votable, MAIN_FCC_VOTER, false, 0); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + vote(chg->fcc_votable, FCC_MAX_QC3P5_VOTER, false, 0); +#endif chg->adapter_cc_mode = 0; chg->thermal_overheat = 0; vote_override(chg->fcc_votable, CC_MODE_VOTER, false, 0); 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); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + vote_override(chg->fcc_main_votable, MAIN_FCC_VOTER, false, 0); + vote(chg->fcc_votable, THERMAL_FCC_OVERRIDE_VOTER, false, 0); +#endif /* write back the default FLOAT charger configuration */ rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG, @@ -6323,12 +9942,14 @@ static void typec_src_removal(struct smb_charger *chg) smblib_err(chg, "Couldn't restore max pulses rc=%d\n", rc); +#ifndef CONFIG_MACH_XIAOMI_SM8250 rc = smblib_masked_write(chg, USBIN_AICL_OPTIONS_CFG_REG, SUSPEND_ON_COLLAPSE_USBIN_BIT, SUSPEND_ON_COLLAPSE_USBIN_BIT); if (rc < 0) smblib_err(chg, "Couldn't turn on SUSPEND_ON_COLLAPSE_USBIN_BIT rc=%d\n", rc); +#endif chg->qc2_unsupported_voltage = QC2_COMPLIANT; } @@ -6336,10 +9957,39 @@ static void typec_src_removal(struct smb_charger *chg) if (chg->use_extcon) smblib_notify_device_mode(chg, false); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + typec_partner_unregister(chg); +#endif chg->typec_legacy = false; del_timer_sync(&chg->apsd_timer); chg->apsd_ext_timeout = false; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* when src removal, set bark timer back to default 16s */ + smblib_set_wdog_bark_timer(chg, BARK_TIMER_NORMAL); + + chg->typec_legacy = false; + chg->detect_low_power_qc3_charger = false; + chg->raise_vbus_to_detect = false; + chg->is_qc_class_a = false; + chg->is_qc_class_b = false; + chg->high_vbus_detected = false; + chg->recheck_charger = false; + chg->snk_debug_acc_detected = false; + chg->pps_thermal_level = -EINVAL; + chg->qc2_unsupported = false; + chg->cc_un_compliant_detected = false; + chg->report_input_absent = false; + chg->qc3_raise_done = false; + + if (chg->pd_verifed) { + chg->pd_verifed = false; + if (smblib_get_fastcharge_mode(chg) == true) { + smblib_set_fastcharge_mode(chg, false); + chg->last_ffc_remove_time = ktime_get(); + } + } +#endif } static void typec_mode_unattached(struct smb_charger *chg) @@ -6515,6 +10165,9 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data) if (smblib_get_prop_dfp_mode(chg) == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) { chg->sink_src_mode = AUDIO_ACCESS_MODE; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (!chg->power_good_en) +#endif typec_ra_ra_insertion(chg); } else if (stat & SNK_SRC_MODE_BIT) { if (smblib_src_lpd(chg)) @@ -6524,20 +10177,42 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data) } else { chg->sink_src_mode = SINK_MODE; typec_src_insertion(chg); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->support_wireless) + smblib_wireless_set_rx_sleep_pin(chg, false); +#endif } +#ifndef CONFIG_MACH_XIAOMI_SM8250 rc = typec_partner_register(chg); if (rc < 0) smblib_err(chg, "failed to register partner rc =%d\n", rc); +#endif } else { switch (chg->sink_src_mode) { case SRC_MODE: typec_sink_removal(chg); break; case SINK_MODE: - case AUDIO_ACCESS_MODE: +#ifdef CONFIG_MACH_XIAOMI_SM8250 typec_src_removal(chg); + if (chg->support_wireless) { + smblib_wireless_set_rx_sleep_pin(chg, true); + } + break; +#endif + case AUDIO_ACCESS_MODE: +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (!chg->power_good_en) { +#endif + typec_src_removal(chg); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->support_wireless) { + smblib_wireless_set_rx_sleep_pin(chg, true); + } +#endif + } break; case UNATTACHED_MODE: default: @@ -6552,6 +10227,7 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data) smblib_apsd_enable(chg, true); } +#ifndef CONFIG_MACH_XIAOMI_SM8250 /* * Restore DRP mode on type-C cable disconnect if role * swap is not in progress, to ensure forced sink or src @@ -6580,6 +10256,7 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data) } mutex_unlock(&chg->typec_lock); +#endif if (chg->lpd_stage == LPD_STAGE_FLOAT_CANCEL) schedule_delayed_work(&chg->lpd_detach_work, @@ -6598,6 +10275,348 @@ irqreturn_t typec_attach_detach_irq_handler(int irq, void *data) return IRQ_HANDLED; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +/*add for wireless reverse charge to disable dc*/ +int smblib_set_sw_disable_dc_en(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc = 0; + + if (val->intval) { + smblib_dbg(chg, PR_OEM, "disable dc en by sw\n"); + /* step1: enter dc suspend */ + rc = vote(chg->dc_suspend_votable, SW_DISABLE_DC_VOTER, + true, 0); + + /* step2: force dcin_en low */ + rc = smblib_masked_write(chg, DCIN_CMD_IL_REG, + DCIN_EN_OVERRIDE_BIT | DCIN_EN_BIT, + DCIN_EN_OVERRIDE_BIT); + if (rc < 0) { + dev_err(chg->dev, "Couldn't configure dc_en override rc=%d\n", rc); + return rc; + } + + /* step3: enable pull-down on dcin_pon and mid_chg */ + rc = smblib_masked_write(chg, DCIN_CMD_PULLDOWN_REG, + DCIN_PULLDOWN_EN_BIT | DCIN_MID_PULLDOWN_BIT, + DCIN_PULLDOWN_EN_BIT | DCIN_MID_PULLDOWN_BIT); + if (rc < 0) { + dev_err(chg->dev, "Couldn't enable dcin_pulldown rc=%d\n", rc); + return rc; + } + /* wait 10ms to pull mid_chg lower than Vsys+Vrevi */ + msleep(10); + + /* step4: exit dc suspend */ + rc = vote(chg->dc_suspend_votable, SW_DISABLE_DC_VOTER, + false, 0); + } + + return 0; +} + +static int smblib_set_dc_pach_safe_when_power_good_enable(struct smb_charger *chg) +{ + + int rc = 0; + + /* step1: force dcin_en low */ + rc = smblib_masked_write(chg, DCIN_CMD_IL_REG, + DCIN_EN_OVERRIDE_BIT | DCIN_EN_BIT, + DCIN_EN_OVERRIDE_BIT); + if (rc < 0) { + dev_err(chg->dev, "Couldn't configure dc_en override rc=%d\n", rc); + return rc; + } + + /* step2: enable pull-down on dcin_pon and mid_chg */ + rc = smblib_masked_write(chg, DCIN_CMD_PULLDOWN_REG, + DCIN_PULLDOWN_EN_BIT | DCIN_MID_PULLDOWN_BIT, + DCIN_PULLDOWN_EN_BIT | DCIN_MID_PULLDOWN_BIT); + if (rc < 0) { + dev_err(chg->dev, "Couldn't enable dcin_pulldown rc=%d\n", rc); + return rc; + } + /* wait 10ms to pull mid_chg lower than Vsys+Vrevi */ + msleep(10); + + /* step3: disable pull-down on dcin_pon and mid_chg */ + rc = smblib_masked_write(chg, DCIN_CMD_PULLDOWN_REG, + DCIN_PULLDOWN_EN_BIT | DCIN_MID_PULLDOWN_BIT, + 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't disable dcin_pulldown rc=%d\n", rc); + return rc; + } + + /* step4: remove dcin_en low */ + rc = smblib_masked_write(chg, DCIN_CMD_IL_REG, + DCIN_EN_OVERRIDE_BIT | DCIN_EN_BIT, + 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't configure dc_en override rc=%d\n", rc); + return rc; + } + + return rc; +} + +static void smblib_dc_plug_out_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + dc_plug_out_delay_work.work); + + chg->fake_dc_on = 0; /*use for delay 1.8s*/ + power_supply_changed(chg->dc_psy); + smblib_dbg(chg, PR_WLS, "Delay timeout and clear dc fake value\n"); + vote(chg->awake_votable, DC_PLUGOUT_WIRELESS_VOTER, false, 0); +} + +static void smblib_wireless_delay_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + wireless_full_delay_work.work); + int rc; + u8 stat; + + 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; + } + + stat = stat & BATTERY_CHARGER_STATUS_MASK; + if (stat == TERMINATE_CHARGE) { + smblib_dbg(chg, PR_OEM, "release wireless lock when full\n"); + /* when wpc charge done, set bark timer to 128s to decrease wakeups */ + smblib_set_wdog_bark_timer(chg, BARK_TIMER_LONG); + vote(chg->awake_votable, DC_AWAKE_VOTER, false, 0); + } +} + +#define POWER_GOOD_OFF_DELAY_MS 1800 +int smblib_set_wirless_power_good_enable(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc = 0; + + chg->power_good_en = val->intval; + smblib_dbg(chg, PR_OEM, "power good: %d\n", chg->power_good_en); + + if (!chg->wireless_bq) + return rc; + + if (chg->power_good_en) { + chg->fake_dc_on = 1; + smblib_dc_chg_q1_enable(chg, true); + cancel_delayed_work(&chg->dc_plug_out_delay_work); + + if (chg->typec_mode == POWER_SUPPLY_TYPEC_SINK + || chg->typec_mode == POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE){ + smblib_dbg(chg, PR_OEM, "typec :%d, en 3902 pass\n", chg->typec_mode); + ncp3902_pass_enable(chg, true); + } + + /* disable OTG Voter for chargting via USB */ + vote(chg->usb_icl_votable, OTG_VOTER, false, 0); + vote(chg->awake_votable, DC_PLUGOUT_WIRELESS_VOTER, false, 0); + rc = smblib_usb_pd_adapter_allowance_override(chg, FORCE_NULL); + if (rc < 0) + pr_err("Failed to set adapter allowance to 5V or 9v to 12v\n"); + + rc = smblib_masked_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, + USBIN_ADAPTER_ALLOW_MASK, USBIN_ADAPTER_ALLOW_5V_TO_12V); + if (rc < 0) { + pr_err("Couldn't set USBIN_ADAPTER_ALLOW_CFG_REG rc=%d\n", rc); + } + + rc = smblib_icl_override(chg, SW_OVERRIDE_NO_CC_MODE); + if (rc < 0) { + pr_err("Couldn't disable ICL override rc=%d\n", rc); + return rc; + } + } else { + /* delay 1.8s to show discharging */ + schedule_delayed_work(&chg->dc_plug_out_delay_work, + msecs_to_jiffies(POWER_GOOD_OFF_DELAY_MS)); + vote(chg->awake_votable, DC_PLUGOUT_WIRELESS_VOTER, true, 0); + /* + * wireless detached and OTG plug-out, open q1 for wired charging. + * wireless detached and OTG plug-in, vote usb-icl to 0 to prevent charging hint. + */ + + /* close ncp pass mode first for OTG and wireless conflict process */ + ncp3902_pass_enable(chg, false); + msleep(50); + + /* close ncp second for OTG and wireless conflict process */ + if (is_ncp3902_pull_low(chg)) { + smblib_ncp3902_enable(chg, true); + msleep(50); + } + + /* open q1 last for OTG and wireless conflict process */ + if (chg->typec_mode != POWER_SUPPLY_TYPEC_SINK + && chg->typec_mode != POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE) + smblib_dc_chg_q1_enable(chg, false); + else + vote(chg->usb_icl_votable, OTG_VOTER, true, 0); + + smblib_wl_bq_enable(chg, false); + if (smblib_get_fastcharge_mode(chg) == true) + smblib_set_fastcharge_mode(chg, false); + + smblib_set_dc_pach_safe_when_power_good_enable(chg); + + rc = smblib_usb_pd_adapter_allowance_override(chg, FORCE_NULL); + if (rc < 0) + pr_err("Failed to set adapter allowance to 5V or 9v to 12v\n"); + + rc = smblib_masked_write(chg, USBIN_ADAPTER_ALLOW_CFG_REG, + USBIN_ADAPTER_ALLOW_MASK, USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V); + if (rc < 0) { + pr_err("Couldn't set USBIN_ADAPTER_ALLOW_CFG_REG rc=%d\n", rc); + } + + chg->en_bq_flag = 0; + rc = smblib_icl_override(chg, HW_AUTO_MODE); + if (rc < 0) { + pr_err("Couldn't disable ICL override rc=%d\n", rc); + return rc; + } + /* close voter of termination when wireless plug out*/ + vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, false, 0); + } + + if (chg->dc_psy) + power_supply_changed(chg->dc_psy); + if (chg->wireless_psy) + power_supply_changed(chg->wireless_psy); + if (chg->batt_psy) + power_supply_changed(chg->batt_psy); + + return 0; +} + + +int smblib_set_wirless_cp_enable(struct smb_charger *chg, + const union power_supply_propval *val) +{ + /* if use usbmid and dcin wireless charge, to do later*/ + if (!chg->wireless_bq) + return 0; + + if ((val->intval) && (chg->power_good_en)) { + printk("enable bq for quick wireless charge\n"); + smblib_dbg(chg, PR_WLS, "enable bq for quick wireless charge\n"); + smblib_wl_bq_enable(chg, true); + smblib_dc_therm_charging(chg, chg->dc_temp_level); + chg->en_bq_flag = 1; + /* enable FFC when cp enabled */ + if (smblib_get_fastcharge_mode(chg) == false) + smblib_set_fastcharge_mode(chg, true); + if (chg->six_pin_step_charge_enable) { + /* start six pin step charge monitor work */ + schedule_delayed_work(&chg->six_pin_batt_step_chg_work, + msecs_to_jiffies(STEP_CHG_DELAYED_START_MS)); + } + } else { + printk("disable bq quick charge\n"); + smblib_dbg(chg, PR_WLS, "disable bq quick charge\n"); + smblib_wl_bq_enable(chg, false); + chg->en_bq_flag = 0; + } + + if (chg->wireless_psy) + power_supply_changed(chg->wireless_psy); + return 0; +} + +static void smblib_charger_type_recheck(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + charger_type_recheck.work); + int recheck_time = TYPE_RECHECK_TIME_5S; + static int last_charger_type, check_count; + int rc, dc_power_on; + union power_supply_propval pval = {0,}; + + smblib_get_prop_dc_present(chg, &pval); + dc_power_on = pval.intval; + smblib_update_usb_type(chg); + smblib_dbg(chg, PR_OEM, "typec_mode:%d,last:%d: real charger type:%d\n", + chg->typec_mode, last_charger_type, chg->real_charger_type); + + if (last_charger_type != chg->real_charger_type) + check_count--; + last_charger_type = chg->real_charger_type; + + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3 || + chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3P5 || + chg->real_charger_type == POWER_SUPPLY_TYPE_USB_CDP || + chg->pd_active || (check_count >= TYPE_RECHECK_COUNT) || + ((chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP) && + (chg->qc2_unsupported == true)) || + ((chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) && + (chg->typec_mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER)) || + ((chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) && + dc_power_on)) { + smblib_dbg(chg, PR_OEM, "hvdcp detect or check_count = %d break\n", + check_count); + check_count = 0; + return; + } + + if (smblib_get_prop_dfp_mode(chg) != POWER_SUPPLY_TYPEC_NONE) + goto check_next; + + if (chg->typec_port && !chg->pr_swap_in_progress) { + + /* + * Schedule the work to differentiate actual removal + * of cable and detach interrupt during role swap, + * unregister the partner only during actual cable + * removal. + */ + cancel_delayed_work(&chg->pr_swap_detach_work); + vote(chg->awake_votable, DETACH_DETECT_VOTER, true, 0); + schedule_delayed_work(&chg->pr_swap_detach_work, + msecs_to_jiffies(TYPEC_DETACH_DETECT_DELAY_MS)); + smblib_force_dr_mode(chg, TYPEC_PORT_DRP); + /* + * To handle cable removal during role + * swap failure. + */ + chg->typec_role_swap_failed = false; + } + + if (!chg->recheck_charger) + chg->precheck_charger_type = chg->real_charger_type; + chg->recheck_charger = true; + + /* need request hsusb phy dpdm to false then true for float charger */ + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) { + rc = smblib_request_dpdm(chg, false); + if (rc < 0) + smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc); + + msleep(500); + } + + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP) + chg->hvdcp_recheck_status = true; + + smblib_rerun_apsd_if_required(chg); + +check_next: + check_count++; + schedule_delayed_work(&chg->charger_type_recheck, + msecs_to_jiffies(recheck_time)); +} +#endif + static void dcin_aicl(struct smb_charger *chg) { int rc, icl, icl_save; @@ -6745,6 +10764,12 @@ 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; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* directly return irq_handled if use bq wireless solution */ + if (chg->wireless_bq) + return IRQ_HANDLED; +#endif + mutex_lock(&chg->dcin_aicl_lock); chg->dcin_uv_count++; @@ -6767,6 +10792,12 @@ irqreturn_t dc_plugin_irq_handler(int irq, void *data) int rc, wireless_vout = 0, wls_set = 0; int sec_charger; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* directly return irq_handled if use bq wireless solution */ + if (chg->wireless_bq) + return IRQ_HANDLED; +#endif + rc = smblib_get_prop_vph_voltage_now(chg, &pval); if (rc < 0) return IRQ_HANDLED; @@ -7000,6 +11031,11 @@ irqreturn_t wdog_bark_irq_handler(int irq, void *data) if (rc < 0) smblib_err(chg, "Couldn't pet the dog rc=%d\n", rc); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->step_chg_enabled || chg->sw_jeita_enabled) + power_supply_changed(chg->batt_psy); +#endif + return IRQ_HANDLED; } @@ -7044,6 +11080,203 @@ static void smblib_usbov_dbc_work(struct work_struct *work) vote(chg->awake_votable, USBOV_DBC_VOTER, false, 0); } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static int smblib_get_step_vfloat_index(struct smb_charger *chg, + int val) +{ + int i; + + /* select correct index by compare start_vbat and range vfloat threshold */ + for (i = 0; i <= ARRAY_SIZE(chg->six_pin_step_cfg) - 1; i++) { + if (val < (chg->six_pin_step_cfg[i].vfloat_step_uv - VBAT_FOR_STEP_HYS_UV)) + break; + } + + if (i >= ARRAY_SIZE(chg->six_pin_step_cfg) - 1) + return ARRAY_SIZE(chg->six_pin_step_cfg) - 1; + + return i; +} + +#define SECOND_FFC_TERM_TEMP 350 +static void smblib_six_pin_batt_step_chg_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + six_pin_batt_step_chg_work.work); + + int rc = 0; + int input_present; + int main_charge_type; + int interval_ms = STEP_CHG_DELAYED_MONITOR_MS; + int fcc_ua = 0, ibat_ua = 0, capacity = 0, health; + int step_soc_th = 0; + int ti_battery_voltage; + union power_supply_propval pval = {0, }; + + rc = smblib_is_input_present(chg, &input_present); + if (rc < 0) + return; + + if (!chg->cp_psy) { + chg->cp_psy = power_supply_get_by_name("bq2597x-standalone"); + if (!chg->cp_psy) + pr_err("cp_psy not found\n"); + } + rc = power_supply_get_property(chg->cp_psy, + POWER_SUPPLY_PROP_TI_BATTERY_VOLTAGE, &pval); + if (rc < 0) + pr_err("Error in getting TI_BATTERY_VOLTAGE, rc=%d\n", rc); + ti_battery_voltage = pval.intval; + + pr_info("input_present: %d\n", input_present); + if (input_present == INPUT_NOT_PRESENT) { + chg->init_start_vbat_checked = false; + chg->trigger_taper_count = 0; + chg->index_vfloat = 0; + chg->flag_second_ffc_term_current = false; + if (is_client_vote_enabled(chg->fv_votable, + SIX_PIN_VFLOAT_VOTER)) + vote(chg->fv_votable, SIX_PIN_VFLOAT_VOTER, false, 0); + if (is_client_vote_enabled(chg->fcc_votable, + SIX_PIN_VFLOAT_VOTER)) + vote(chg->fcc_votable, SIX_PIN_VFLOAT_VOTER, false, 0); + return; + } + + if (chg->start_step_vbat >= VBAT_FOR_STEP_MIN_UV) { + pr_err("start step vbat is too high, no need do step charge\n"); + return; + } + + /* set init start vfloat according to chg->start_step_vbat */ + if (!chg->init_start_vbat_checked) { + chg->index_vfloat = + smblib_get_step_vfloat_index(chg, chg->start_step_vbat); + vote(chg->fv_votable, SIX_PIN_VFLOAT_VOTER, + true, chg->six_pin_step_cfg[chg->index_vfloat].vfloat_step_uv); + vote(chg->fcc_votable, SIX_PIN_VFLOAT_VOTER, + true, chg->six_pin_step_cfg[chg->index_vfloat].fcc_step_ua); + chg->init_start_vbat_checked = true; + } + + rc = smblib_get_prop_batt_charge_type(chg, &pval); + if (rc < 0) { + pr_err("Couldn't get batt charge type rc=%d\n", rc); + return; + } + main_charge_type = pval.intval; + pr_info("main_charge_type: %d\n", main_charge_type); + + /* + * Add capacity compare to optimize cool charge switch to + * normal charge step too early issue, if capacity is below 40, + * do not switch to next step. + */ + rc = smblib_get_prop_batt_capacity(chg, &pval); + if (rc < 0) { + pr_err("Couldn't get batt charge type rc=%d\n", rc); + return; + } + capacity = pval.intval; + step_soc_th = chg->step_soc_threshold; + pr_info("step_soc_threshold: %d\n", step_soc_th); + + rc = smblib_get_prop_batt_health(chg, &pval); + if (rc < 0) + smblib_err(chg, "Couldn't get batt health rc=%d\n", rc); + + health = pval.intval; + if (health != POWER_SUPPLY_HEALTH_GOOD && !chg->health_not_good) { + chg->health_not_good = true; + vote(chg->fv_votable, SIX_PIN_VFLOAT_VOTER, false, 0); + } else if (health == POWER_SUPPLY_HEALTH_GOOD + && chg->health_not_good) { + chg->health_not_good = false; + vote(chg->fv_votable, SIX_PIN_VFLOAT_VOTER, + true, chg->six_pin_step_cfg[chg->index_vfloat].vfloat_step_uv); + } + + if (chg->support_second_ffc_term_current) { + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_TEMP, &pval); + if (rc < 0) + smblib_err(chg, "Couldn't get bms temp:%d\n", rc); + + if (main_charge_type == POWER_SUPPLY_CHARGE_TYPE_TAPER + && chg->flag_second_ffc_term_current == false + && pval.intval < chg->chg_warm_threshold + && pval.intval > SECOND_FFC_TERM_TEMP + && capacity == 100) { + chg->flag_second_ffc_term_current = true; + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_FFC_TERMINATION_CURRENT, &pval); + if (!rc) { + pval.intval -= chg->support_second_ffc_term_current_diff; + smb5_config_iterm(chg, pval.intval, 50); + smblib_dbg(chg, PR_MISC, "set second termi: %d\n", + pval.intval); + } + } + } + + if (main_charge_type == POWER_SUPPLY_CHARGE_TYPE_TAPER + && capacity > step_soc_th) { + fcc_ua = get_effective_result(chg->fcc_votable) + - TAPER_DECREASE_FCC_UA; + pr_err("taper from main charger, reducing FCC to %duA\n", + fcc_ua); + + if (fcc_ua < MIN_TAPER_FCC_THR_UA) + goto out; + + vote(chg->fcc_votable, SIX_PIN_VFLOAT_VOTER, true, fcc_ua); + } + +out: + rc = smblib_get_batt_current_now(chg, &pval); + if (rc < 0) { + pr_err("Couldn't get ibat from bms rc=%d\n", rc); + return; + } + + ibat_ua = - pval.intval; + pr_info("ibat_ua:%d, ti_battery_voltage:%d.\n", + ibat_ua, ti_battery_voltage); + + if (main_charge_type == POWER_SUPPLY_CHARGE_TYPE_TAPER + && (capacity > step_soc_th) + && (chg->index_vfloat < (MAX_STEP_ENTRIES - 1)) + && (ibat_ua <= (chg->six_pin_step_cfg[chg->index_vfloat + 1].fcc_step_ua + + TAPER_IBAT_TRH_HYS_UA))) + chg->trigger_taper_count++; + else + chg->trigger_taper_count = 0; + + if (chg->trigger_taper_count >= MAX_COUNT_OF_IBAT_STEP) { + chg->index_vfloat++; + chg->trigger_taper_count = 0; + if (chg->index_vfloat >= MAX_STEP_ENTRIES) + chg->index_vfloat = MAX_STEP_ENTRIES - 1; + if (chg->index_vfloat < MAX_STEP_ENTRIES) { + vote(chg->fcc_votable, SIX_PIN_VFLOAT_VOTER, + true, chg->six_pin_step_cfg[chg->index_vfloat].fcc_step_ua); + vote(chg->fv_votable, SIX_PIN_VFLOAT_VOTER, + true, chg->six_pin_step_cfg[chg->index_vfloat].vfloat_step_uv); + } + } + + fcc_ua = get_effective_result(chg->fcc_votable); + + if (main_charge_type == POWER_SUPPLY_CHARGE_TYPE_TAPER) + interval_ms = STEP_CHG_DELAYED_QUICK_MONITOR_MS; + else + interval_ms = STEP_CHG_DELAYED_MONITOR_MS; + + schedule_delayed_work(&chg->six_pin_batt_step_chg_work, + msecs_to_jiffies(interval_ms)); +} +#endif + #define USB_OV_DBC_PERIOD_MS 1000 irqreturn_t usbin_ov_irq_handler(int irq, void *data) { @@ -7103,9 +11336,19 @@ int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg, { int rc; u8 stat = 0, orientation; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int dc_power_on = 0; + union power_supply_propval dc_val = {0,}; +#endif smblib_dbg(chg, PR_MISC, "Requested PR_SWAP %d\n", val->intval); chg->pr_swap_in_progress = val->intval; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->wireless_bq) { + smblib_get_prop_dc_present(chg, &dc_val); + dc_power_on = dc_val.intval; + } +#endif /* check for cable removal during pr_swap */ if (!chg->pr_swap_in_progress) { @@ -7121,6 +11364,10 @@ int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg, if (rc < 0) smblib_err(chg, "Couldn't set tCC debounce rc=%d\n", rc); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* if wireless power on, do not set the bit. wireless will set later */ + if(!dc_power_on) +#endif rc = smblib_masked_write(chg, TYPE_C_EXIT_STATE_CFG_REG, BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT, val->intval ? BYPASS_VSAFE0V_DURING_ROLE_SWAP_BIT : 0); @@ -7248,6 +11495,124 @@ static void smblib_uusb_otg_work(struct work_struct *work) vote(chg->awake_votable, OTG_DELAY_VOTER, false, 0); } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_set_prop_rechg_vbat_thresh(struct smb_charger *chg, + const union power_supply_propval *val) +{ + int rc; + int auto_recharge_vbat_mv = val->intval; + + rc = smblib_masked_write(chg, CHGR_CFG2_REG, RECHG_MASK, + (auto_recharge_vbat_mv != -EINVAL) ? + VBAT_BASED_RECHG_BIT : 0); + if (rc < 0) { + dev_err(chg->dev, "Couldn't configure VBAT-rechg CHG_CFG2_REG rc=%d\n", + rc); + return rc; + } + + /* program the auto-recharge VBAT threshold */ + if (auto_recharge_vbat_mv != -EINVAL) { + u32 temp = VBAT_TO_VRAW_ADC(auto_recharge_vbat_mv); + + temp = ((temp & 0xFF00) >> 8) | ((temp & 0xFF) << 8); + rc = smblib_batch_write(chg, + CHGR_ADC_RECHARGE_THRESHOLD_MSB_REG, (u8 *)&temp, 2); + if (rc < 0) { + dev_err(chg->dev, "Couldn't configure ADC_RECHARGE_THRESHOLD REG rc=%d\n", + rc); + return rc; + } + /* Program the sample count for VBAT based recharge to 3 */ + rc = smblib_masked_write(chg, CHGR_NO_SAMPLE_TERM_RCHG_CFG_REG, + NO_OF_SAMPLE_FOR_RCHG, + 2 << NO_OF_SAMPLE_FOR_RCHG_SHIFT); + if (rc < 0) { + dev_err(chg->dev, "Couldn't configure CHGR_NO_SAMPLE_FOR_TERM_RCHG_CFG rc=%d\n", + rc); + return rc; + } + chg->auto_recharge_vbat = auto_recharge_vbat_mv; + smblib_dbg(chg, PR_OEM, "set chg->auto_recharge_vbat=%d\n", chg->auto_recharge_vbat); + } + + return rc; +} + +#define TEMP_COOL_RECHARGE_VBAT 4300 +#define TEMP_WARM_RECHARGE_VBAT 3980 +#define TEMP_NORM_RECHARGE_VBAT 4380 + +static int smblib_dynamic_recharge_vbat(struct smb_charger *chg) +{ + union power_supply_propval val; + int rc, temp, recharge_vbat; + static int last_recharge_vbat; + + if (chg->auto_recharge_vbat < 0) + return 0; + + rc = smblib_get_prop_from_bms(chg, + POWER_SUPPLY_PROP_TEMP, &val); + if (rc < 0) { + smblib_dbg(chg, PR_REGISTER, "Couldn't get debug battery prop rc=%d\n", rc); + return -EINVAL; + } + temp = val.intval; + + if (temp <= BATT_COOL_THRESHOLD) { + recharge_vbat = TEMP_COOL_RECHARGE_VBAT; + } + else if (temp >= BATT_WARM_THRESHOLD) { + recharge_vbat = TEMP_WARM_RECHARGE_VBAT; + } else { + rc = smblib_get_prop_from_bms(chg, + POWER_SUPPLY_PROP_RECHARGE_VBAT, &val); + if (rc < 0) { + smblib_dbg(chg, PR_REGISTER, "Couldn't get recharge vbat rc=%d\n", rc); + recharge_vbat = TEMP_NORM_RECHARGE_VBAT; + } else + recharge_vbat = val.intval; + } + + if (recharge_vbat != last_recharge_vbat) { + val.intval = recharge_vbat; + last_recharge_vbat = recharge_vbat; + } else + return 0; + + rc = power_supply_set_property(chg->batt_psy, + POWER_SUPPLY_PROP_RECHARGE_VBAT, + &val); + if (rc < 0) { + dev_err(chg->dev, "Couldn't set POWER_SUPPLY_PROP_CHARGER_TEMP_MAX rc=%d\n", + rc); + return -EINVAL; + } + + return 0; +} + + +static void batt_update_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + batt_update_work); + int rc; + union power_supply_propval pval = {0,}; + + if (!chg->batt_psy) + return; + + rc = power_supply_get_property(chg->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &pval); + if (rc < 0) { + pr_err("Couldn't get batt capacity status rc=%d\n", rc); + } + chg->capacity = pval.intval; +} +#endif + static void bms_update_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, @@ -7255,6 +11620,10 @@ static void bms_update_work(struct work_struct *work) smblib_suspend_on_debug_battery(chg); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + smblib_dynamic_recharge_vbat(chg); +#endif + if (chg->batt_psy) power_supply_changed(chg->batt_psy); } @@ -7320,6 +11689,15 @@ static void smblib_icl_change_work(struct work_struct *work) smblib_dbg(chg, PR_INTERRUPT, "icl_settled=%d\n", settled_ua); } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static void smblib_dc_power_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + dc_power_work.work); + smblib_ncp3902_enable(chg, false); +} +#endif + static void smblib_pl_enable_work(struct work_struct *work) { struct smb_charger *chg = container_of(work, struct smb_charger, @@ -7452,6 +11830,10 @@ static void smblib_chg_termination_work(struct work_struct *work) chg_termination_work); int rc, input_present, delay = CHG_TERM_WA_ENTRY_DELAY_MS; int vbat_now_uv, max_fv_uv; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + enum power_supply_property prop; + int capacity_raw; +#endif /* * Hold awake votable to prevent pm_relax being called prior to @@ -7463,6 +11845,14 @@ static void smblib_chg_termination_work(struct work_struct *work) if ((rc < 0) || !input_present) goto out; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->ext_fg) { + rc = smblib_get_prop_from_bms(chg, + POWER_SUPPLY_PROP_CAPACITY_RAW, &pval); + pval.intval = pval.intval / 100; + capacity_raw = pval.intval; + } else +#endif rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_REAL_CAPACITY, &pval); if ((rc < 0) || (pval.intval < 100)) { @@ -7470,6 +11860,19 @@ static void smblib_chg_termination_work(struct work_struct *work) vote(chg->dc_suspend_votable, CHG_TERMINATION_VOTER, false, 0); goto out; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->ext_fg) { + max_fv_uv = get_effective_result(chg->fv_votable); + } else { + /* Get the battery float voltage */ + rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_VOLTAGE_MAX, + &pval); + if (rc < 0) + goto out; + + max_fv_uv = pval.intval; + } +#endif /* Get the battery float voltage */ rc = smblib_get_prop_from_bms(chg, POWER_SUPPLY_PROP_VOLTAGE_MAX, @@ -7484,6 +11887,12 @@ static void smblib_chg_termination_work(struct work_struct *work) if (rc < 0) goto out; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->ext_fg) + prop = POWER_SUPPLY_PROP_CAPACITY_RAW; + else + prop = POWER_SUPPLY_PROP_CC_SOC; +#endif /* * On change in the value of learned capacity, re-initialize the * reference cc_soc value due to change in cc_soc characteristic value @@ -7524,6 +11933,12 @@ static void smblib_chg_termination_work(struct work_struct *work) &pval); if (rc < 0) goto out; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + vbat_now_uv = pval.intval; + rc = smblib_get_prop_from_bms(chg, prop, &pval); + if (rc < 0) + goto out; +#endif } /* @@ -7543,6 +11958,23 @@ static void smblib_chg_termination_work(struct work_struct *work) * overshoot range of the cc_soc value at termination and make sure that * vbat is indeed rising above vfloat. */ +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->ext_fg) { + if (capacity_raw < 100) { + vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, false, 0); + vote(chg->dc_suspend_votable, CHG_TERMINATION_VOTER, false, 0); + delay = CHG_TERM_WA_ENTRY_DELAY_MS; + } else if (((vbat_now_uv > chg->term_vbat_uv) && (vbat_now_uv > max_fv_uv))) { + if (input_present & INPUT_PRESENT_USB) + vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, + true, 0); + if (input_present & INPUT_PRESENT_DC) + vote(chg->dc_suspend_votable, CHG_TERMINATION_VOTER, + true, 0); + delay = CHG_TERM_WA_EXIT_DELAY_MS; + } + } else { +#endif if (pval.intval < DIV_ROUND_CLOSEST(chg->cc_soc_ref * 10050, 10000)) { vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, false, 0); vote(chg->dc_suspend_votable, CHG_TERMINATION_VOTER, false, 0); @@ -7560,6 +11992,9 @@ static void smblib_chg_termination_work(struct work_struct *work) true, 0); delay = CHG_TERM_WA_EXIT_DELAY_MS; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + } +#endif smblib_dbg(chg, PR_MISC, "Chg Term WA readings: cc_soc: %d, cc_soc_ref: %d, delay: %d vbat_now %d term_vbat %d\n", pval.intval, chg->cc_soc_ref, delay, vbat_now_uv, @@ -7567,6 +12002,9 @@ static void smblib_chg_termination_work(struct work_struct *work) alarm_start_relative(&chg->chg_termination_alarm, ms_to_ktime(delay)); out: vote(chg->awake_votable, CHG_TERMINATION_VOTER, false, 0); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, false, 0); +#endif } static enum alarmtimer_restart chg_termination_alarm_cb(struct alarm *alarm, @@ -7886,6 +12324,17 @@ static void smblib_cp_status_change_work(struct work_struct *work) pm_relax(chg->dev); } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static void smblib_batt_verify_update_work(struct work_struct *work) +{ + struct smb_charger *chg = container_of(work, struct smb_charger, + batt_verify_update_work); + + if (chg->batt_verified) + vote(chg->fcc_votable, BATT_VERIFY_VOTER, false, 0); +} +#endif + static int smblib_create_votables(struct smb_charger *chg) { int rc = 0; @@ -8059,13 +12508,24 @@ int smblib_init(struct smb_charger *chg) mutex_init(&chg->dcin_aicl_lock); mutex_init(&chg->dpdm_lock); spin_lock_init(&chg->typec_pr_lock); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + INIT_WORK(&chg->batt_update_work, batt_update_work); +#endif 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); INIT_WORK(&chg->dcin_aicl_work, dcin_aicl_work); INIT_WORK(&chg->cp_status_change_work, smblib_cp_status_change_work); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + INIT_WORK(&chg->batt_verify_update_work, smblib_batt_verify_update_work); + INIT_WORK(&chg->plugin_check_time_work, smblib_plugin_check_time_work); + INIT_DELAYED_WORK(&chg->fake_plug_out_check_work, smblib_fake_plug_out_check_work); +#endif INIT_DELAYED_WORK(&chg->clear_hdc_work, clear_hdc_work); INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + INIT_DELAYED_WORK(&chg->dc_power_work, smblib_dc_power_work); +#endif INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work); INIT_DELAYED_WORK(&chg->uusb_otg_work, smblib_uusb_otg_work); INIT_DELAYED_WORK(&chg->bb_removal_work, smblib_bb_removal_work); @@ -8074,14 +12534,40 @@ int smblib_init(struct smb_charger *chg) INIT_DELAYED_WORK(&chg->thermal_regulation_work, smblib_thermal_regulation_work); INIT_DELAYED_WORK(&chg->usbov_dbc_work, smblib_usbov_dbc_work); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + INIT_DELAYED_WORK(&chg->six_pin_batt_step_chg_work, + smblib_six_pin_batt_step_chg_work); +#endif INIT_DELAYED_WORK(&chg->pr_swap_detach_work, smblib_pr_swap_detach_work); +#ifndef CONFIG_MACH_XIAOMI_SM8250 INIT_DELAYED_WORK(&chg->pr_lock_clear_work, smblib_pr_lock_clear_work); +#endif timer_setup(&chg->apsd_timer, apsd_timer_cb, 0); INIT_DELAYED_WORK(&chg->role_reversal_check, smblib_typec_role_check_work); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + INIT_DELAYED_WORK(&chg->reg_work, smblib_reg_work); + INIT_DELAYED_WORK(&chg->thermal_setting_work, smblib_thermal_setting_work); +#ifndef CONFIG_FUEL_GAUGE_BQ27Z561 + INIT_DELAYED_WORK(&chg->reduce_fcc_work, reduce_fcc_work); +#endif + INIT_DELAYED_WORK(&chg->charger_type_recheck, smblib_charger_type_recheck); + INIT_DELAYED_WORK(&chg->raise_qc3_vbus_work, smblib_raise_qc3_vbus_work); + INIT_DELAYED_WORK(&chg->pr_lock_clear_work, smblib_pr_lock_clear_work); + INIT_DELAYED_WORK(&chg->conn_therm_work, smblib_conn_therm_work); + INIT_DELAYED_WORK(&chg->after_ffc_chg_dis_work, smblib_after_ffc_chg_dis_work); + INIT_DELAYED_WORK(&chg->after_ffc_chg_en_work, smblib_after_ffc_chg_en_work); + INIT_DELAYED_WORK(&chg->dc_plug_out_delay_work, smblib_dc_plug_out_work); + INIT_DELAYED_WORK(&chg->wireless_full_delay_work, smblib_wireless_delay_work); + INIT_DELAYED_WORK(&chg->report_soc_decimal_work, smblib_report_soc_decimal_work); + INIT_DELAYED_WORK(&chg->step_charge_notify_work, smblib_step_charge_notify_work); + INIT_DELAYED_WORK(&chg->cc_un_compliant_charge_work, smblib_cc_un_compliant_charge_work); + INIT_DELAYED_WORK(&chg->clean_cp_to_sw_work, smblib_clean_cp_to_sw_work); + INIT_DELAYED_WORK(&chg->check_init_boot, smb_check_init_boot); +#endif if (chg->wa_flags & CHG_TERMINATION_WA) { INIT_WORK(&chg->chg_termination_work, @@ -8121,13 +12607,34 @@ int smblib_init(struct smb_charger *chg) chg->fake_input_current_limited = -EINVAL; chg->fake_batt_status = -EINVAL; chg->sink_src_mode = UNATTACHED_MODE; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /*disable qcom default battery profile soft-jeita, use step chg jeita*/ + chg->jeita_configured = JEITA_CFG_FAILURE; +#else chg->jeita_configured = false; +#endif chg->sec_chg_selected = POWER_SUPPLY_CHARGER_SEC_NONE; chg->cp_reason = POWER_SUPPLY_CP_NONE; chg->thermal_status = TEMP_BELOW_RANGE; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->pps_thermal_level = -EINVAL; +#ifndef CONFIG_FUEL_GAUGE_BQ27Z561 + chg->esr_work_status = ESR_CHECK_FCC_NOLIMIT; +#endif +#endif chg->typec_irq_en = true; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->hvdcp_recheck_status = false; + chg->batt_temp_irq_enabled = false; + chg->no_raise_vbus_status = false; + chg->fake_plug_out = false; + chg->cp_to_sw_status = false; +#endif chg->cp_topo = -EINVAL; chg->dr_mode = TYPEC_PORT_DRP; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chg->capacity = -EINVAL; +#endif switch (chg->mode) { case PARALLEL_MASTER: @@ -8154,6 +12661,14 @@ int smblib_init(struct smb_charger *chg) } chg->bms_psy = power_supply_get_by_name("bms"); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chg->ext_bbc) { + chg->bbc_psy = power_supply_get_by_name("bbc"); + } +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + chg->batt_verify_psy = power_supply_get_by_name("batt_verify"); +#endif +#endif if (chg->sec_pl_present) { chg->pl.psy = power_supply_get_by_name("parallel"); @@ -8221,13 +12736,24 @@ int smblib_deinit(struct smb_charger *chg) cancel_work_sync(&chg->chg_termination_work); } del_timer_sync(&chg->apsd_timer); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + cancel_work_sync(&chg->batt_update_work); +#endif cancel_work_sync(&chg->bms_update_work); cancel_work_sync(&chg->jeita_update_work); cancel_work_sync(&chg->pl_update_work); cancel_work_sync(&chg->dcin_aicl_work); cancel_work_sync(&chg->cp_status_change_work); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + cancel_work_sync(&chg->batt_verify_update_work); + cancel_work_sync(&chg->plugin_check_time_work); + cancel_delayed_work_sync(&chg->fake_plug_out_check_work); +#endif cancel_delayed_work_sync(&chg->clear_hdc_work); cancel_delayed_work_sync(&chg->icl_change_work); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + cancel_delayed_work_sync(&chg->dc_power_work); +#endif cancel_delayed_work_sync(&chg->pl_enable_work); cancel_delayed_work_sync(&chg->uusb_otg_work); cancel_delayed_work_sync(&chg->bb_removal_work); @@ -8236,7 +12762,24 @@ int smblib_deinit(struct smb_charger *chg) cancel_delayed_work_sync(&chg->thermal_regulation_work); cancel_delayed_work_sync(&chg->usbov_dbc_work); cancel_delayed_work_sync(&chg->role_reversal_check); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + cancel_delayed_work_sync(&chg->six_pin_batt_step_chg_work); +#endif cancel_delayed_work_sync(&chg->pr_swap_detach_work); + cancel_delayed_work_sync(&chg->reg_work); +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#ifndef CONFIG_FUEL_GAUGE_BQ27Z561 + cancel_delayed_work_sync(&chg->reduce_fcc_work); +#endif +#endif + cancel_delayed_work_sync(&chg->charger_type_recheck); + cancel_delayed_work_sync(&chg->raise_qc3_vbus_work); + cancel_delayed_work_sync(&chg->conn_therm_work); + cancel_delayed_work_sync(&chg->report_soc_decimal_work); + cancel_delayed_work_sync(&chg->cc_un_compliant_charge_work); + cancel_delayed_work_sync(&chg->step_charge_notify_work); + cancel_delayed_work_sync(&chg->clean_cp_to_sw_work); + power_supply_unreg_notifier(&chg->nb); smblib_destroy_votables(chg); qcom_step_chg_deinit(); @@ -8253,3 +12796,16 @@ int smblib_deinit(struct smb_charger *chg) return 0; } + +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static int __init early_parse_off_charge_flag(char *p) +{ + if (p) { + if (!strcmp(p, "charger")) + off_charge_flag = true; + } + + return 0; +} +early_param("androidboot.mode", early_parse_off_charge_flag); +#endif diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h index 8ee11fb3a3b2..22499363390b 100644 --- a/drivers/power/supply/qcom/smb5-lib.h +++ b/drivers/power/supply/qcom/smb5-lib.h @@ -16,6 +16,12 @@ #include #include #include "storm-watch.h" +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#include +#include +#include +#include +#endif #include "battery.h" enum print_reason { @@ -25,6 +31,9 @@ enum print_reason { PR_PARALLEL = BIT(3), PR_OTG = BIT(4), PR_WLS = BIT(5), +#ifdef CONFIG_MACH_XIAOMI_SM8250 + PR_OEM = BIT(6), +#endif }; #define DEFAULT_VOTER "DEFAULT_VOTER" @@ -57,6 +66,11 @@ enum print_reason { #define PL_FCC_LOW_VOTER "PL_FCC_LOW_VOTER" #define WBC_VOTER "WBC_VOTER" #define HW_LIMIT_VOTER "HW_LIMIT_VOTER" +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define CHG_AWAKE_VOTER "CHG_AWAKE_VOTER" +#define DC_AWAKE_VOTER "DC_AWAKE_VOTER" +#define DC_PLUGOUT_WIRELESS_VOTER "DC_PLUGOUT_WIRELESS_VOTER" +#endif #define PL_SMB_EN_VOTER "PL_SMB_EN_VOTER" #define FORCE_RECHARGE_VOTER "FORCE_RECHARGE_VOTER" #define LPD_VOTER "LPD_VOTER" @@ -65,6 +79,9 @@ enum print_reason { #define JEITA_ARB_VOTER "JEITA_ARB_VOTER" #define MOISTURE_VOTER "MOISTURE_VOTER" #define HVDCP2_ICL_VOTER "HVDCP2_ICL_VOTER" +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define HVDCP2_FCC_VOTER "HVDCP2_FCC_VOTER" +#endif #define AICL_THRESHOLD_VOTER "AICL_THRESHOLD_VOTER" #define USBOV_DBC_VOTER "USBOV_DBC_VOTER" #define CHG_TERMINATION_VOTER "CHG_TERMINATION_VOTER" @@ -78,32 +95,199 @@ enum print_reason { #define MAIN_FCC_VOTER "MAIN_FCC_VOTER" #define DCIN_AICL_VOTER "DCIN_AICL_VOTER" #define WLS_PL_CHARGING_VOTER "WLS_PL_CHARGING_VOTER" +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define OTG_VOTER "OTG_VOTER" +#endif #define ICL_CHANGE_VOTER "ICL_CHANGE_VOTER" #define OVERHEAT_LIMIT_VOTER "OVERHEAT_LIMIT_VOTER" #define TYPEC_SWAP_VOTER "TYPEC_SWAP_VOTER" +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define SW_DISABLE_DC_VOTER "SW_DISABLE_DC_VOTER" +#define CLASSA_QC_FCC_VOTER "CLASSA_QC_FCC_VOTER" +#define QC_A_CP_ICL_MAX_VOTER "QC_A_CP_ICL_MAX_VOTER" +#define JEITA_VOTER "JEITA_VOTER" +#define AFTER_FFC_VOTER "AFTER_FFC_VOTER" +#define BATT_VERIFY_VOTER "BATT_VERIFY_VOTER" +#define BBC_CHARGER_VOTER "BBC_CHARGER_VOTER" +#define QC2_UNSUPPORTED_VOTER "QC2_UNSUPPORTED_VOTER" +#define THERMAL_FCC_OVERRIDE_VOTER "THERMAL_FCC_OVERRIDE_VOTER" +/* use for QC3P5 */ +#define QC3P5_VOTER "QC3P5_VOTER" +#define FCC_MAX_QC3P5_VOTER "FCC_MAX_QC3P5_VOTER" + +/* thermal micros */ +#define MAX_TEMP_LEVEL 16 +/* defined for distinguish qc class_a and class_b */ +#define VOL_THR_FOR_QC_CLASS_AB 12400000 +#define COMP_FOR_LOW_RESISTANCE_CABLE 100000 +#define QC_CLASS_A_CURRENT_UA 3600000 +#define HVDCP_CLASS_A_MAX_UA 2500000 +#define HVDCP_CLASS_A_FOR_CP_UA 1900000 +#define MAX_PULSE 38 +#define MAX_PLUSE_COUNT_ALLOWED 30 +#define HIGH_NUM_PULSE_THR 12 +#endif + #define BOOST_BACK_STORM_COUNT 3 #define WEAK_CHG_STORM_COUNT 8 +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define MAX_QC3P5_PLUSE_COUNT_ALLOWED 230 +#define QC3P5_DP_RAPIDLY_TUNE_ALLOWED 120 +#define QC3P5_DP_RAPIDLY_TUNE_PULSE 10 + +/* defined for qc2_unsupported */ +#define QC2_UNSUPPORTED_UA 1800000 +/* defined for HVDCP2 */ +#define HVDCP2_CURRENT_UA 1400000 +/* defined for un_compliant Type-C cable */ +#define CC_UN_COMPLIANT_START_DELAY_MS 700 +#endif + #define VBAT_TO_VRAW_ADC(v) div_u64((u64)v * 1000000UL, 194637UL) #define ITERM_LIMITS_PMI632_MA 5000 #define ITERM_LIMITS_PM8150B_MA 10000 #define ADC_CHG_ITERM_MASK 32767 +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define BAT_TEMP_COLD 0 +#define BAT_TEMP_TOO_HOT 580 +#endif #define SDP_100_MA 100000 #define SDP_CURRENT_UA 500000 #define CDP_CURRENT_UA 1500000 +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define DCP_CURRENT_UA 1600000 +#define HVDCP_START_CURRENT_UA 1000000 +#define HVDCP_CURRENT_UA 2800000 +#else #define DCP_CURRENT_UA 1500000 #define HVDCP_CURRENT_UA 3000000 +#endif #define TYPEC_DEFAULT_CURRENT_UA 900000 #define TYPEC_MEDIUM_CURRENT_UA 1500000 #define TYPEC_HIGH_CURRENT_UA 3000000 +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define HVDCP3P5_40W_CURRENT_UA 4000000 +#define PD_UNVERIFED_CURRENT 4800000 +#define FLOAT_CHARGER_UA 1000000 + +/* used for bq charge pump solution */ +#define MAIN_CHG_SUSPEND_VOTER "MAIN_CHG_SUSPEND_VOTER" +#define PD_VERIFED_VOTER "PD_VERIFED_VOTER" +#define WIRELESS_BY_USB_IN_VOTER "WIRELESS_BY_USB_IN_VOTER" + +#define ADC_CHG_TERM_MASK 32767 +#endif #define DCIN_ICL_MIN_UA 100000 #define DCIN_ICL_MAX_UA 1500000 #define DCIN_ICL_STEP_UA 100000 #define ROLE_REVERSAL_DELAY_MS 500 +#ifdef CONFIG_MACH_XIAOMI_SM8250 +/* six pin new battery step charge micros */ +#define MAX_STEP_ENTRIES 3 +#define MAX_COUNT_OF_IBAT_STEP 2 +#define TAPER_DECREASE_FCC_UA 100000 +#define TAPER_SLOWDECREASE_FCC_UA 50000 +#define TAPER_IBAT_TRH_HYS_UA 50000 +#define MIN_TAPER_FCC_THR_UA 2500000 +#define SECOND_FFC_TERM_CURRENT_DIFF 98 + +#define STEP_CHG_DELAYED_MONITOR_MS 10000 +#define STEP_CHG_DELAYED_QUICK_MONITOR_MS 2000 +#define STEP_CHG_DELAYED_SLOWQUICK_MONITOR_MS 1000 +#define STEP_CHG_DELAYED_START_MS 100 +#define VBAT_FOR_STEP_MIN_UV 4300000 +#define VBAT_FOR_STEP_HYS_UV 20000 +#define WARM_VFLOAT_UV 4100000 + +#define MAIN_CHG_SUSPEND_ICL 50000 +#define MAIN_ICL_MIN 100000 +#define MAIN_ICL_MIN_VOTER "MAIN_ICL_MIN_VOTER" +#define SIX_PIN_VFLOAT_VOTER "SIX_PIN_VFLOAT_VOTER" +#define NON_FFC_VFLOAT_VOTER "NON_FFC_VFLOAT_VOTER" +#define SW_CONN_THERM_VOTER "SW_CONN_THERM_VOTER" + +#define QC3P5_CHARGER_ICL 2000000 + +#ifndef CONFIG_FUEL_GAUGE_BQ27Z561 +#define ESR_WORK_VOTER "ESR_WORK_VOTER" +#define ESR_WORK_TIME_2S 2000 +#define ESR_WORK_TIME_97S 97000 + +enum esr_work_status { + ESR_CHECK_FCC_NOLIMIT, + ESR_CHECK_FCC_LIMITED, +}; +#endif + +#define REPORT_SOC_DECIMAL_MS 100 + +#define RECHARGE_SOC_THR 99 + +/* cutoff voltage threshold */ +#define CUTOFF_VOL_THR 3400000 +#define CUTOFF_VOL_HYS 50000 + +/* wdog bark timer */ +#define BARK_TIMER_LONG 128 +#define BARK_TIMER_NORMAL 16 + +/* notify qcom step charge callback func interval time */ +#define NOTIFY_STEP_CALLBACK_MS 2000 + +/* wireless thermal related */ +#define ADAPTER_NONE 0x00 +#define ADAPTER_SDP 0x01 +#define ADAPTER_CDP 0x02 +#define ADAPTER_DCP 0x03 +#define ADAPTER_QC2 0x05 +#define ADAPTER_QC3 0x06 +#define ADAPTER_PD 0x07 +#define ADAPTER_AUTH_FAILED 0x08 +#define ADAPTER_XIAOMI_QC3 0x09 +#define ADAPTER_XIAOMI_PD 0x0a +#define ADAPTER_ZIMI_CAR_POWER 0x0b +#define ADAPTER_XIAOMI_PD_40W 0x0c +#define ADAPTER_XIAOMI_PD_50W 0x0e +#define ADAPTER_XIAOMI_PD_60W 0x0f +#define ADAPTER_VOICE_BOX 0x0d + +/* defined for charger type recheck */ +#define CHARGER_RECHECK_DELAY_MS 30000 +#define TYPE_RECHECK_TIME_5S 5000 +#define TYPE_RECHECK_COUNT 3 + +/* define for reverse state of wireless charging */ +#define REVERSE_GPIO_STATE_UNSET 0 +#define REVERSE_GPIO_STATE_START 1 +#define REVERSE_GPIO_STATE_END 2 + +enum hvdcp3_type { + HVDCP3_NONE = 0, + HVDCP3_CLASSA_18W, + HVDCP3_CLASSB_27W, + HVDCP3P5_CLASSA_18W, + HVDCP3P5_CLASSB_27W, +}; + +enum quick_charge_type { + QUICK_CHARGE_NORMAL = 0, + QUICK_CHARGE_FAST, + QUICK_CHARGE_FLASH, + QUICK_CHARGE_TURBE, + QUICK_CHARGE_MAX, +}; + +struct quick_charge { + enum power_supply_type adap_type; + enum quick_charge_type adap_cap; +}; +#endif + enum smb_mode { PARALLEL_MASTER = 0, PARALLEL_SLAVE, @@ -295,11 +479,23 @@ enum icl_override_mode { SW_OVERRIDE_USB51_MODE, /* ICL other than USB51 */ SW_OVERRIDE_HC_MODE, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /* add NO CC MODE*/ + SW_OVERRIDE_NO_CC_MODE, +#endif }; /* EXTCON_USB and EXTCON_USB_HOST are mutually exclusive */ static const u32 smblib_extcon_exclusive[] = {0x3, 0}; +#ifdef CONFIG_MACH_XIAOMI_SM8250 +/* six pin battery data struct */ +struct six_pin_step_data { + u32 vfloat_step_uv; + u32 fcc_step_ua; +}; +#endif + struct smb_regulator { struct regulator_dev *rdev; struct regulator_desc rdesc; @@ -385,8 +581,14 @@ struct smb_charger { struct smb_chg_freq chg_freq; int otg_delay_ms; int weak_chg_icl_ua; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int thermal_fcc_override; +#endif u32 sdam_base; bool pd_not_supported; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + bool batt_verified; +#endif /* locks */ struct mutex smb_lock; @@ -403,11 +605,29 @@ struct smb_charger { struct power_supply *usb_psy; struct power_supply *dc_psy; struct power_supply *bms_psy; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + struct power_supply *bbc_psy; + struct power_supply_desc usb_psy_desc; +#endif struct power_supply *usb_main_psy; struct power_supply *usb_port_psy; struct power_supply *wls_psy; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + struct power_supply *idtp_psy; + struct power_supply *wireless_psy; + struct power_supply *wls_chip_psy; + struct power_supply *wip_psy; + struct power_supply *ln_psy; + struct power_supply *cp_chip_psy; +#endif struct power_supply *cp_psy; +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + struct power_supply *batt_verify_psy; +#endif enum power_supply_type real_charger_type; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + enum power_supply_type wireless_charger_type; +#endif /* notifiers */ struct notifier_block nb; @@ -430,6 +650,12 @@ struct smb_charger { struct typec_partner *typec_partner; struct typec_partner_desc typec_partner_desc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + struct pinctrl *smb5_pinctrl; + struct pinctrl_state *smb5_gpio_active; + struct pinctrl_state *smb5_gpio_suspend; +#endif + /* votables */ struct votable *dc_suspend_votable; struct votable *fcc_votable; @@ -450,6 +676,9 @@ struct smb_charger { struct votable *qnovo_disable_votable; /* work */ +#ifdef CONFIG_MACH_XIAOMI_SM8250 + struct work_struct batt_update_work; +#endif struct work_struct bms_update_work; struct work_struct pl_update_work; struct work_struct jeita_update_work; @@ -457,9 +686,17 @@ struct smb_charger { struct work_struct chg_termination_work; struct work_struct dcin_aicl_work; struct work_struct cp_status_change_work; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + struct work_struct batt_verify_update_work; + struct work_struct plugin_check_time_work; + struct delayed_work fake_plug_out_check_work; +#endif struct delayed_work ps_change_timeout_work; struct delayed_work clear_hdc_work; struct delayed_work icl_change_work; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + struct delayed_work dc_power_work; +#endif struct delayed_work pl_enable_work; struct delayed_work uusb_otg_work; struct delayed_work bb_removal_work; @@ -467,9 +704,33 @@ struct smb_charger { struct delayed_work lpd_detach_work; struct delayed_work thermal_regulation_work; struct delayed_work usbov_dbc_work; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + struct delayed_work six_pin_batt_step_chg_work; +#endif struct delayed_work pr_swap_detach_work; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + struct delayed_work reg_work; + struct delayed_work thermal_setting_work; +#ifndef CONFIG_FUEL_GAUGE_BQ27Z561 + struct delayed_work reduce_fcc_work; +#endif + struct delayed_work charger_type_recheck; + struct delayed_work raise_qc3_vbus_work; +#endif struct delayed_work pr_lock_clear_work; struct delayed_work role_reversal_check; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + struct delayed_work conn_therm_work; + struct delayed_work after_ffc_chg_dis_work; + struct delayed_work after_ffc_chg_en_work; + struct delayed_work dc_plug_out_delay_work; + struct delayed_work wireless_full_delay_work; + struct delayed_work report_soc_decimal_work; + struct delayed_work step_charge_notify_work; + struct delayed_work cc_un_compliant_charge_work; + struct delayed_work clean_cp_to_sw_work; + struct delayed_work check_init_boot; +#endif struct alarm lpd_recheck_timer; struct alarm moisture_protection_alarm; @@ -490,10 +751,16 @@ struct smb_charger { int voltage_min_uv; int voltage_max_uv; int pd_active; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int apdo_max; +#endif bool pd_hard_reset; bool pr_lock_in_progress; bool pr_swap_in_progress; bool early_usb_attach; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + bool early_dc_attach; +#endif bool ok_to_pd; bool typec_legacy; bool typec_irq_en; @@ -504,6 +771,26 @@ struct smb_charger { int boost_threshold_ua; int system_temp_level; int thermal_levels; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int dc_temp_level; + int dc_thermal_levels; + int pps_thermal_level; + int *thermal_mitigation_dcp; + int *thermal_mitigation_qc2; + int *thermal_mitigation_pd_base; + int *thermal_mitigation_icl; + int *thermal_fcc_qc3_normal; + int *thermal_fcc_qc3_cp; + int *thermal_fcc_qc3_classb_cp; + int *thermal_fcc_pps_cp; + int *thermal_mitigation_dc; + int *thermal_mitigation_voice; + int *thermal_mitigation_epp; + int *thermal_mitigation_bpp_qc3; + int *thermal_mitigation_bpp_qc2; + int *thermal_mitigation_bpp; + int *thermal_mitigation_dc_20W; +#endif int *thermal_mitigation; int dcp_icl_ua; int fake_capacity; @@ -531,8 +818,14 @@ struct smb_charger { bool use_extcon; bool otg_present; bool hvdcp_disable; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + bool fake_hvdcp3; +#endif int hw_max_icl_ua; int auto_recharge_soc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int auto_recharge_vbat; +#endif enum sink_src_mode sink_src_mode; enum power_supply_typec_power_role power_role; enum jeita_cfg_stat jeita_configured; @@ -573,8 +866,14 @@ struct smb_charger { int term_vbat_uv; int usbin_forced_max_uv; int init_thermal_ua; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int pd_verifed; +#endif u32 comp_clamp_level; int wls_icl_ua; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int capacity; +#endif int cutoff_count; bool dcin_aicl_done; bool hvdcp3_standalone_config; @@ -589,6 +888,13 @@ struct smb_charger { int qc2_max_pulses; enum qc2_non_comp_voltage qc2_unsupported_voltage; bool dbc_usbov; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + bool qc2_unsupported; + bool no_raise_vbus_status; + bool vbus_rising; + bool fake_plug_out; + bool cp_to_sw_status; +#endif /* extcon for VBUS / ID notification to USB for uUSB */ struct extcon_dev *extcon; @@ -596,6 +902,9 @@ struct smb_charger { /* battery profile */ int batt_profile_fcc_ua; int batt_profile_fv_uv; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int non_fcc_batt_profile_fv_uv; +#endif int usb_icl_delta_ua; int pulse_cnt; @@ -612,9 +921,111 @@ struct smb_charger { u32 irq_status; /* wireless */ +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int flag_dc_present; + int power_good_en; + int fake_dc_on; + int fake_dc_flag; + bool en_bq_flag; + int64_t rpp; + int64_t cep; + int64_t tx_bt_mac; + int64_t oob_rpp_msg_cnt; + int64_t oob_cep_msg_cnt; + int reverse_chg_state; + int reverse_gpio_state; + + /* product related */ + bool support_wireless; + bool wireless_bq; + bool fake_usb_insertion; + /* external gpios for charging and wireless charging ctrl */ + unsigned int ncp3902_en_gpio; + unsigned int ncp3902_pass_en_gpio; + /* wireless direct charging related */ + unsigned int dc_chg_gpio; + unsigned int bq_en_gpio; + unsigned int rx_hw_sleep_gpio; + /* used for bq charge pump solution */ + struct usbpd *pd; + bool use_bq_pump; + bool ext_fg; + bool ext_bbc; + /* used for 6pin new battery step charge */ + bool six_pin_step_charge_enable; + bool init_start_vbat_checked; + struct six_pin_step_data six_pin_step_cfg[MAX_STEP_ENTRIES]; + u32 start_step_vbat; + int trigger_taper_count; + int index_vfloat; + int step_soc_threshold; + + int chg_warm_threshold; + int chg_cool_threshold; +#endif int dcin_uv_count; ktime_t dcin_uv_last_time; int last_wls_vout; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int fake_conn_temp; + u64 entry_time; + int entry_connector_therm; +#ifndef CONFIG_FUEL_GAUGE_BQ27Z561 + /* reduce fcc for esr cal*/ + int esr_work_status; + bool cp_charge_enabled; + int charge_type; + int charge_status; + int batt_health; +#endif + /* charger type recheck */ + int recheck_charger; + int precheck_charger_type; + int chg_term_current_thresh_hi_from_dts; + /* raise qc3 vbus flag */ + bool qc_class_ab; + bool is_qc_class_a; + bool is_qc_class_b; + bool raise_vbus_to_detect; + bool detect_low_power_qc3_charger; + bool high_vbus_detected; + bool support_ffc; + bool qc3_raise_done; + /* workarounds */ + bool support_conn_therm; + int conn_detect_count; + int vbus_disable_gpio; + int vbus_disable; + u64 last_ffc_remove_time; + u64 after_raise_vbus_time; + u64 plugin_attach_check_time; + u64 plugin_detch_check_time; + + bool warm_fake_charging; + bool health_not_good; + bool hvdcp_recheck_status; + + /* QC3P5 related */ + bool qc3p5_supported; + bool qc3p5_auth_complete; + bool qc3p5_authenticated; + bool qc3p5_authentication_started; + bool qc3p5_dp_tune_rapidly; + int qc3p5_power_limit_w; + + /* workarounds */ + bool snk_debug_acc_detected; + bool cc_un_compliant_detected; + bool report_input_absent; + bool batt_temp_irq_enabled; + bool vbat_critical_low_triggered; + + int support_second_ffc_term_current_diff; + bool support_second_ffc_term_current; + bool flag_second_ffc_term_current; + + int night_chg_flag; +#endif }; int smblib_read(struct smb_charger *chg, u16 addr, u8 *val); @@ -666,6 +1077,9 @@ irqreturn_t typec_state_change_irq_handler(int irq, void *data); irqreturn_t typec_attach_detach_irq_handler(int irq, void *data); irqreturn_t dcin_uv_irq_handler(int irq, void *data); irqreturn_t dc_plugin_irq_handler(int irq, void *data); +#ifdef CONFIG_MACH_XIAOMI_SM8250 +irqreturn_t dc_power_on_irq_handler(int irq, void *data); +#endif irqreturn_t high_duty_cycle_irq_handler(int irq, void *data); irqreturn_t switcher_power_ok_irq_handler(int irq, void *data); irqreturn_t wdog_snarl_irq_handler(int irq, void *data); @@ -676,10 +1090,18 @@ irqreturn_t usbin_ov_irq_handler(int irq, void *data); irqreturn_t sdam_sts_change_irq_handler(int irq, void *data); int smblib_get_prop_input_suspend(struct smb_charger *chg, union power_supply_propval *val); +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_get_prop_battery_input_suspend(struct smb_charger *chg, + union power_supply_propval *val); +#endif 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); +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_get_prop_batt_capacity_level(struct smb_charger *chg, + union power_supply_propval *val); +#endif 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, @@ -700,6 +1122,10 @@ int smblib_get_prop_batt_iterm(struct smb_charger *chg, union power_supply_propval *val); int smblib_set_prop_input_suspend(struct smb_charger *chg, const union power_supply_propval *val); +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_set_prop_battery_input_suspend(struct smb_charger *chg, + const union power_supply_propval *val); +#endif int smblib_set_prop_batt_capacity(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_batt_status(struct smb_charger *chg, @@ -725,6 +1151,10 @@ int smblib_get_prop_voltage_wls_output(struct smb_charger *chg, union power_supply_propval *val); int smblib_set_prop_voltage_wls_output(struct smb_charger *chg, const union power_supply_propval *val); +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_get_prop_wireless_version(struct smb_charger *chg, + union power_supply_propval *val); +#endif int smblib_set_prop_dc_reset(struct smb_charger *chg); int smblib_get_prop_usb_present(struct smb_charger *chg, union power_supply_propval *val); @@ -775,6 +1205,9 @@ int smblib_get_prop_input_current_max(struct smb_charger *chg, union power_supply_propval *val); int smblib_set_prop_thermal_overheat(struct smb_charger *chg, int therm_overheat); +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_get_prop_connector_temp(struct smb_charger *chg); +#endif int smblib_get_skin_temp_status(struct smb_charger *chg); int smblib_get_prop_vph_voltage_now(struct smb_charger *chg, union power_supply_propval *val); @@ -798,6 +1231,10 @@ int smblib_set_prop_ship_mode(struct smb_charger *chg, const union power_supply_propval *val); int smblib_set_prop_rechg_soc_thresh(struct smb_charger *chg, const union power_supply_propval *val); +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_set_prop_rechg_vbat_thresh(struct smb_charger *chg, + const union power_supply_propval *val); +#endif void smblib_suspend_on_debug_battery(struct smb_charger *chg); int smblib_rerun_apsd_if_required(struct smb_charger *chg); void smblib_rerun_apsd(struct smb_charger *chg); @@ -819,6 +1256,49 @@ int smblib_typec_port_type_set(const struct typec_capability *cap, int smblib_get_prop_from_bms(struct smb_charger *chg, enum power_supply_property psp, union power_supply_propval *val); +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_set_prop_wireless_wakelock(struct smb_charger *chg, + const union power_supply_propval *val); + +int smblib_set_wirless_cp_enable(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_set_wirless_power_good_enable(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_set_sw_disable_dc_en(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_set_prop_tx_mac(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_set_prop_rx_cr(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_set_prop_bt_state(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_set_prop_input_current_max(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_get_prop_wireless_version(struct smb_charger *chg, + union power_supply_propval *val); +int smblib_set_prop_dc_temp_level(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_get_prop_dc_temp_level(struct smb_charger *chg, + union power_supply_propval *val); +int smblib_set_prop_tx_mac(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_set_prop_rx_cr(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_set_prop_rx_cep(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_set_prop_bt_state(struct smb_charger *chg, + const union power_supply_propval *val); + +int smblib_set_sw_disable_dc_en(struct smb_charger *chg, + const union power_supply_propval *val); + +int smblib_set_prop_typec_boost_otg_disable(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_set_prop_battery_charging_enabled(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_set_vbus_disable(struct smb_charger *chg, + bool disable); +#endif int smblib_get_iio_channel(struct smb_charger *chg, const char *propname, struct iio_channel **chan); int smblib_read_iio_channel(struct smb_charger *chg, struct iio_channel *chan, @@ -835,8 +1315,29 @@ void smblib_apsd_enable(struct smb_charger *chg, bool enable); int smblib_force_vbus_voltage(struct smb_charger *chg, u8 val); int smblib_get_irq_status(struct smb_charger *chg, union power_supply_propval *val); +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_set_prop_type_recheck(struct smb_charger *chg, + const union power_supply_propval *val); +int smblib_get_prop_type_recheck(struct smb_charger *chg, + union power_supply_propval *val); +int smblib_night_charging_func(struct smb_charger *chg, + union power_supply_propval *val); +int smblib_get_quick_charge_type(struct smb_charger *chg); +#endif int smblib_get_qc3_main_icl_offset(struct smb_charger *chg, int *offset_ua); +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_dp_dm_bq(struct smb_charger *chg, int val); +int smblib_get_prop_battery_charging_enabled(struct smb_charger *chg, + union power_supply_propval *val); +int smblib_set_fastcharge_mode(struct smb_charger *chg, bool enable); +int smblib_get_fastcharge_mode(struct smb_charger *chg); +struct usbpd *smb_get_usbpd(void); +#endif int smblib_init(struct smb_charger *chg); int smblib_deinit(struct smb_charger *chg); +#ifdef CONFIG_MACH_XIAOMI_SM8250 +int smblib_get_prop_wireless_fw_version(struct smb_charger *chg, + union power_supply_propval *val); +#endif #endif /* __SMB5_CHARGER_H */ diff --git a/drivers/power/supply/qcom/smb5-reg.h b/drivers/power/supply/qcom/smb5-reg.h index 311e810c31c0..f7eb2946f6ca 100644 --- a/drivers/power/supply/qcom/smb5-reg.h +++ b/drivers/power/supply/qcom/smb5-reg.h @@ -275,6 +275,13 @@ enum { HVDCP_PULSE_COUNT_MAX_QC2_INVALID = 0xC0 }; +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define USBIN_ADAPTER_ALLOW_CFG_REG (USBIN_BASE + 0x60) +#define USBIN_ADAPTER_ALLOW_MASK GENMASK(3, 0) +#define USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V 0x07 +#define USBIN_ADAPTER_ALLOW_5V_TO_12V 0x0c +#endif + #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) @@ -317,12 +324,24 @@ enum { /* DCIN Interrupt Bits */ #define DCIN_PLUGIN_RT_STS_BIT BIT(4) +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define DCIN_PON_RT_STS_BIT BIT(6) +#endif #define DCIN_CMD_IL_REG (DCIN_BASE + 0x40) #define DCIN_SUSPEND_BIT BIT(0) #define DCIN_EN_OVERRIDE_BIT BIT(1) +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define DCIN_EN_BIT BIT(2) +#endif #define DCIN_EN_MASK GENMASK(2, 1) +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define DCIN_CMD_PULLDOWN_REG (DCIN_BASE + 0x45) +#define DCIN_PULLDOWN_EN_BIT BIT(0) +#define DCIN_MID_PULLDOWN_BIT BIT(1) +#endif + #define DCIN_CMD_PON_REG (DCIN_BASE + 0x45) #define DCIN_PON_BIT BIT(0) #define MID_CHG_BIT BIT(1) @@ -344,6 +363,11 @@ enum { #define SNK_RP_3P0_BIT BIT(1) #define SNK_RP_SHORT_BIT BIT(0) +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define TYPE_C_SNK_DEBUG_ACC_STATUS_REG (TYPEC_BASE + 0x07) +#define SNK_DEBUG_ACC_RPSTD_PRSTD_BIT BIT(0) +#endif + #define TYPE_C_SRC_STATUS_REG (TYPEC_BASE + 0x08) #define DETECTED_SNK_TYPE_MASK GENMASK(4, 0) #define SRC_HIGH_BATT_BIT BIT(5) @@ -355,6 +379,9 @@ enum { #define TYPE_C_STATE_MACHINE_STATUS_REG (TYPEC_BASE + 0x09) #define TYPEC_ATTACH_DETACH_STATE_BIT BIT(5) +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define TYPEC_ATTACHWAIT_SRC 0x05 +#endif #define TYPE_C_MISC_STATUS_REG (TYPEC_BASE + 0x0B) #define TYPEC_WATER_DETECTION_STATUS_BIT BIT(7) @@ -376,6 +403,12 @@ enum { #define U_USB_FMB2_BIT BIT(1) #define U_USB_FLOAT2_BIT BIT(0) +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define TYPE_C_DEBUG_ACC_SNK_CFG (TYPEC_BASE + 0x4A) +#define TYPEC_DEBUG_ACC_SNK_SEL_ICL BIT(2) +#define TYPEC_DEBUG_ACC_SNK_DIS_AICL BIT(3) +#endif + #define TYPE_C_MODE_CFG_REG (TYPEC_BASE + 0x44) #define TYPEC_TRY_MODE_MASK GENMASK(4, 3) #define EN_TRY_SNK_BIT BIT(4) diff --git a/drivers/power/supply/qcom/step-chg-jeita.c b/drivers/power/supply/qcom/step-chg-jeita.c index 35c08078e678..fd1bc1b0b8fb 100644 --- a/drivers/power/supply/qcom/step-chg-jeita.c +++ b/drivers/power/supply/qcom/step-chg-jeita.c @@ -15,6 +15,9 @@ #include "step-chg-jeita.h" #define STEP_CHG_VOTER "STEP_CHG_VOTER" +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define STEP_BMS_CHG_VOTER "STEP_BMS_CHG_VOTER" +#endif #define JEITA_VOTER "JEITA_VOTER" #define is_between(left, right, value) \ @@ -38,6 +41,13 @@ struct jeita_fv_cfg { struct range_data fv_cfg[MAX_STEP_CHG_ENTRIES]; }; +#ifdef CONFIG_MACH_XIAOMI_SM8250 +struct cold_step_chg_cfg { + struct step_chg_jeita_param param; + struct range_data fcc_cfg[MAX_COLD_STEP_CHG_ENTRIES]; +}; +#endif + struct step_chg_info { struct device *dev; ktime_t step_last_update_time; @@ -48,28 +58,57 @@ struct step_chg_info { bool config_is_read; bool step_chg_cfg_valid; bool sw_jeita_cfg_valid; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + bool cold_step_chg_cfg_valid; +#endif bool soc_based_step_chg; bool ocv_based_step_chg; bool vbat_avg_based_step_chg; bool batt_missing; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + bool use_bq_pump; + bool use_bq_gauge; +#endif bool taper_fcc; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + bool six_pin_battery; +#endif int jeita_fcc_index; int jeita_fv_index; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int jeita_cold_fcc_index; +#endif int step_index; int get_config_retry_count; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int jeita_hot_th; + int jeita_cold_th; + int jeita_cool_th; + int jeita_warm_th; +#endif struct step_chg_cfg *step_chg_config; struct jeita_fcc_cfg *jeita_fcc_config; struct jeita_fv_cfg *jeita_fv_config; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + struct cold_step_chg_cfg *cold_step_chg_config; +#endif struct votable *fcc_votable; struct votable *fv_votable; struct votable *usb_icl_votable; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + struct votable *chg_disable_votable; + struct votable *cp_disable_votable; +#endif struct wakeup_source *step_chg_ws; struct power_supply *batt_psy; struct power_supply *bms_psy; struct power_supply *usb_psy; struct power_supply *dc_psy; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + struct power_supply *wls_psy; +#endif struct delayed_work status_change_work; struct delayed_work get_config_work; struct notifier_block nb; @@ -150,6 +189,22 @@ static bool is_input_present(struct step_chg_info *chip) return false; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static bool is_dc_wls_available(struct step_chg_info *chip) +{ + if (!chip->dc_psy) + chip->dc_psy = power_supply_get_by_name("dc"); + + if (!chip->wls_psy) + chip->wls_psy = power_supply_get_by_name("wireless"); + + if (!chip->dc_psy || !chip->wls_psy) + return false; + + return true; +} +#endif + int read_range_data_from_node(struct device_node *node, const char *prop_str, struct range_data *ranges, int max_threshold, u32 max_value) @@ -305,11 +360,23 @@ static int get_step_chg_jeita_setting_from_profile(struct step_chg_info *chip) of_property_read_bool(profile_node, "qcom,ocv-based-step-chg"); if (chip->ocv_based_step_chg) { chip->step_chg_config->param.psy_prop = +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_VOLTAGE_NOW; +#else POWER_SUPPLY_PROP_VOLTAGE_OCV; +#endif chip->step_chg_config->param.prop_name = "OCV"; chip->step_chg_config->param.rise_hys = 0; chip->step_chg_config->param.fall_hys = 0; chip->step_chg_config->param.use_bms = true; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chip->cold_step_chg_config->param.psy_prop = + POWER_SUPPLY_PROP_VOLTAGE_NOW; + chip->cold_step_chg_config->param.prop_name = "OCV"; + chip->cold_step_chg_config->param.rise_hys = 100000; + chip->cold_step_chg_config->param.fall_hys = 100000; + chip->cold_step_chg_config->param.use_bms = true; +#endif } chip->vbat_avg_based_step_chg = @@ -366,6 +433,55 @@ static int get_step_chg_jeita_setting_from_profile(struct step_chg_info *chip) chip->sw_jeita_cfg_valid = false; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chip->cold_step_chg_cfg_valid = true; + rc = read_range_data_from_node(profile_node, + "qcom,cold-step-chg-ranges", + chip->cold_step_chg_config->fcc_cfg, + max_fv_uv, max_fcc_ma * 1000); + if (rc < 0) { + pr_debug("Read qcom,jeita-fv-ranges failed from battery profile, rc=%d\n", + rc); + chip->cold_step_chg_cfg_valid = false; + } + + rc = of_property_read_u32(profile_node, "qcom,jeita-too-hot", + &chip->jeita_hot_th); + if (rc < 0) { + pr_err("do not use external fg and set jeita to hot to invaled\n"); + chip->jeita_hot_th = -EINVAL; + } + + rc = of_property_read_u32(profile_node, "qcom,jeita-too-cold", + &chip->jeita_cold_th); + if (rc < 0) { + pr_err("do not use external fg and set jeita too cold to invaled\n"); + chip->jeita_cold_th = -EINVAL; + } + + chip->jeita_warm_th = BATT_WARM_THRESHOLD; + rc = of_property_read_u32(profile_node, "qcom,jeita-warm-th", + &chip->jeita_warm_th); + if (rc < 0) { + pr_err("do not use dtsi config and set jeita warm to invaled\n"); + } + + chip->jeita_cool_th = BATT_COOL_THRESHOLD; + rc = of_property_read_u32(profile_node, "qcom,jeita-cool-th", + &chip->jeita_cool_th); + if (rc < 0) { + pr_err("do not use dtsi config and set jeita cool to invaled\n"); + } + chip->use_bq_pump = + of_property_read_bool(profile_node, "qcom,use-bq-pump"); + + chip->use_bq_gauge = + of_property_read_bool(profile_node, "qcom,use-ext-gauge"); + + chip->six_pin_battery = + of_property_read_bool(profile_node, "mi,six-pin-battery"); +#endif + return rc; } @@ -545,10 +661,30 @@ static int handle_step_chg_config(struct step_chg_info *chip) union power_supply_propval pval = {0, }; int rc = 0, fcc_ua = 0, current_index; u64 elapsed_us; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int fv_uv = 0, update_now = 0; + static int usb_present; + + if (!is_usb_available(chip)) + return 0; + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &pval); + if (rc < 0) { + pr_err("Get battery present status failed, rc=%d\n", rc); + return rc; + } + if (pval.intval && pval.intval != usb_present) + update_now = true; + usb_present = pval.intval; +#endif elapsed_us = ktime_us_delta(ktime_get(), chip->step_last_update_time); /* skip processing, event too early */ +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US && !update_now) +#else if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US) +#endif return 0; rc = power_supply_get_property(chip->batt_psy, @@ -617,17 +753,112 @@ static int handle_step_chg_config(struct step_chg_info *chip) get_client_vote(chip->fcc_votable, STEP_CHG_VOTER), chip->taper_fcc); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + /*bq27z561 get voltage max and current max*/ + if (chip->use_bq_gauge) { + rc = power_supply_get_property(chip->bms_psy, + POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval); + if (rc >= 0 && chip->fv_votable && pval.intval > 0) + vote(chip->fv_votable, STEP_BMS_CHG_VOTER, true, pval.intval); + fv_uv = pval.intval; + + rc = power_supply_get_property(chip->bms_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, &pval); + if (rc >= 0 && chip->fcc_votable && pval.intval > 0) + vote(chip->fcc_votable, STEP_BMS_CHG_VOTER, false, pval.intval); + fcc_ua = pval.intval; + + pr_info("bms step charge fcc:%d fv:%d\n", fcc_ua, fv_uv); + } +#endif + update_time: chip->step_last_update_time = ktime_get(); return 0; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 +static int handle_fast_charge(struct step_chg_info *chip, int temp) +{ + union power_supply_propval pval = {0, }; + static bool fast_mode_dis; + int rc, dc_present, is_cp_en; + int pd_authen; + + if (is_dc_wls_available(chip)) { + rc = power_supply_get_property(chip->dc_psy, + POWER_SUPPLY_PROP_PRESENT, &pval); + if (rc < 0) + pr_err("Couldn't get dc present rc = %d\n", rc); + else + dc_present = pval.intval; + + rc = power_supply_get_property(chip->wls_psy, + POWER_SUPPLY_PROP_WIRELESS_CP_EN, &pval); + if (rc < 0) + pr_err("Couldn't get cp_en rc = %d\n", rc); + else + is_cp_en = pval.intval; + } + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PD_AUTHENTICATION, &pval); + if (rc < 0) { + pr_err("Get fastcharge mode status failed, rc=%d\n", rc); + return rc; + } + pd_authen = pval.intval; + + if (pd_authen || (dc_present && is_cp_en)) { + if ((temp >= chip->jeita_warm_th || temp <= chip->jeita_cool_th) && !fast_mode_dis) { + pr_err("temp:%d disable fastcharge mode\n", temp); + pval.intval = false; + rc = power_supply_set_property(chip->usb_psy, + POWER_SUPPLY_PROP_FASTCHARGE_MODE, &pval); + if (rc < 0) { + pr_err("Set fastcharge mode failed, rc=%d\n", rc); + return rc; + } + fast_mode_dis = true; + } else if ((temp < chip->jeita_warm_th - chip->jeita_fv_config->param.fall_hys) && + (temp > chip->jeita_cool_th + chip->jeita_fv_config->param.rise_hys) && fast_mode_dis) { + pr_err("temp:%d enable fastcharge mode\n", temp); + pval.intval = true; + rc = power_supply_set_property(chip->usb_psy, + POWER_SUPPLY_PROP_FASTCHARGE_MODE, &pval); + if (rc < 0) { + pr_err("Set fastcharge mode failed, rc=%d\n", rc); + return rc; + } + fast_mode_dis = false; + } + } else { + fast_mode_dis = false; + } + + return rc; +} +#endif + +#ifdef CONFIG_MACH_XIAOMI_SM8250 +/* set JEITA_SUSPEND_HYST_UV to 70mV to avoid recharge frequently when jeita warm */ +#define JEITA_SUSPEND_HYST_UV 120000 +#define JEITA_HYSTERESIS_TEMP_THRED 150 +#define JEITA_SIX_PIN_BATT_HYST_UV 100000 +#define WARM_VFLOAT_UV 4100000 +#else #define JEITA_SUSPEND_HYST_UV 50000 +#endif static int handle_jeita(struct step_chg_info *chip) { union power_supply_propval pval = {0, }; int rc = 0, fcc_ua = 0, fv_uv = 0; u64 elapsed_us; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + int temp = 0, volt_now = 0, cold_fcc_ua = 0, update_now = 0; + static bool usb_present; + int curr_vfloat_uv, curr_vbat_uv; +#endif rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_SW_JEITA_ENABLED, &pval); @@ -646,9 +877,27 @@ static int handle_jeita(struct step_chg_info *chip) return 0; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (!is_usb_available(chip)) + return 0; + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &pval); + if (rc < 0) { + pr_err("Get battery present status failed, rc=%d\n", rc); + return rc; + } + if (pval.intval && pval.intval != usb_present) + update_now = true; + usb_present = pval.intval; +#endif + elapsed_us = ktime_us_delta(ktime_get(), chip->jeita_last_update_time); /* skip processing, event too early */ +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US && !update_now) +#else if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US) +#endif return 0; if (chip->jeita_fcc_config->param.use_bms) @@ -664,16 +913,99 @@ static int handle_jeita(struct step_chg_info *chip) return rc; } +#ifdef CONFIG_MACH_XIAOMI_SM8250 + temp = pval.intval; + + if (chip->cold_step_chg_cfg_valid) { + if (chip->cold_step_chg_config->param.use_bms) + rc = power_supply_get_property(chip->bms_psy, + chip->cold_step_chg_config->param.psy_prop, &pval); + else + rc = power_supply_get_property(chip->batt_psy, + chip->cold_step_chg_config->param.psy_prop, &pval); + if (rc < 0) { + pr_err("Couldn't read %s property rc=%d\n", + chip->cold_step_chg_config->param.prop_name, rc); + return rc; + } + + volt_now = pval.intval; + } + + if (!chip->chg_disable_votable) + chip->chg_disable_votable = find_votable("CHG_DISABLE"); + + if (!chip->cp_disable_votable) + chip->cp_disable_votable = find_votable("CP_DISABLE"); + + /* qcom charge pump use cp_disable_voter, others do not need */ + if (!chip->use_bq_pump) { + if (!chip->chg_disable_votable || !chip->cp_disable_votable) + goto update_time; + } else { + if (!chip->chg_disable_votable) + goto update_time; + } + + if(chip->jeita_hot_th >= 0 && chip->jeita_cold_th >= (-100)) { + if (temp >= chip->jeita_hot_th || + temp <= chip->jeita_cold_th) { + pr_info("sw-jeita: temp is :%d, stop charing\n", temp); + vote(chip->chg_disable_votable, JEITA_VOTER, true, 0); + } else { + vote(chip->chg_disable_votable, JEITA_VOTER, false, 0); + } + } + + if (!chip->use_bq_pump) { + if (temp <= chip->jeita_cool_th || temp >= chip->jeita_warm_th) { + vote(chip->cp_disable_votable, JEITA_VOTER, true, 0); + } + else + vote(chip->cp_disable_votable, JEITA_VOTER, false, 0); + } + + if (temp <= JEITA_HYSTERESIS_TEMP_THRED) { + chip->jeita_fv_config->param.rise_hys = 5; + chip->jeita_fv_config->param.fall_hys = 5; + chip->jeita_fcc_config->param.rise_hys = 5; + chip->jeita_fcc_config->param.fall_hys = 5; + } else { + chip->jeita_fv_config->param.rise_hys = 20; + chip->jeita_fv_config->param.fall_hys = 20; + chip->jeita_fcc_config->param.rise_hys = 20; + chip->jeita_fcc_config->param.fall_hys = 20; + } +#endif + rc = get_val(chip->jeita_fcc_config->fcc_cfg, chip->jeita_fcc_config->param.rise_hys, chip->jeita_fcc_config->param.fall_hys, chip->jeita_fcc_index, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + temp, +#else pval.intval, +#endif &chip->jeita_fcc_index, &fcc_ua); if (rc < 0) fcc_ua = 0; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chip->cold_step_chg_cfg_valid == true) { + rc = get_val(chip->cold_step_chg_config->fcc_cfg, + chip->cold_step_chg_config->param.rise_hys, + chip->cold_step_chg_config->param.fall_hys, + chip->jeita_cold_fcc_index, + volt_now, + &chip->jeita_cold_fcc_index, + &cold_fcc_ua); + if (rc < 0) + cold_fcc_ua = 0; + } +#endif + if (!chip->fcc_votable) chip->fcc_votable = find_votable("FCC"); if (!chip->fcc_votable) @@ -681,12 +1013,22 @@ static int handle_jeita(struct step_chg_info *chip) return -EINVAL; vote(chip->fcc_votable, JEITA_VOTER, fcc_ua ? true : false, fcc_ua); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (chip->cold_step_chg_cfg_valid) { + if (chip->jeita_fcc_index == 0 && chip->jeita_cold_fcc_index != 0) + vote(chip->fcc_votable, JEITA_VOTER, cold_fcc_ua ? true : false, cold_fcc_ua); + } +#endif rc = get_val(chip->jeita_fv_config->fv_cfg, chip->jeita_fv_config->param.rise_hys, chip->jeita_fv_config->param.fall_hys, chip->jeita_fv_index, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + temp, +#else pval.intval, +#endif &chip->jeita_fv_index, &fv_uv); if (rc < 0) @@ -702,6 +1044,10 @@ static int handle_jeita(struct step_chg_info *chip) if (!chip->usb_icl_votable) goto set_jeita_fv; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + handle_fast_charge(chip, temp); +#endif + /* * If JEITA float voltage is same as max-vfloat of battery then * skip any further VBAT specific checks. @@ -717,13 +1063,57 @@ static int handle_jeita(struct step_chg_info *chip) * Suspend USB input path if battery voltage is above * JEITA VFLOAT threshold. */ +#ifndef CONFIG_MACH_XIAOMI_SM8250 if (chip->jeita_arb_en && fv_uv > 0) { +#else + if (fv_uv > 0) { +#endif rc = power_supply_get_property(chip->batt_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + if (rc < 0) { + pr_err("Get battery voltage failed, rc = %d\n", rc); + goto set_jeita_fv; + } + curr_vbat_uv = pval.intval; + + if (!chip->six_pin_battery) { + if ((curr_vbat_uv > fv_uv) && (temp >= chip->jeita_warm_th)) + vote(chip->usb_icl_votable, JEITA_VOTER, true, 0); + else if (curr_vbat_uv < (fv_uv - JEITA_SUSPEND_HYST_UV)) + vote(chip->usb_icl_votable, JEITA_VOTER, false, 0); + } else { + curr_vfloat_uv = get_effective_result(chip->fv_votable); + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_TYPE, &pval); + if (rc < 0) { + pr_err("Get charge type failed, rc = %d\n", rc); + goto set_jeita_fv; + } + + if (curr_vfloat_uv != WARM_VFLOAT_UV) { + if (curr_vbat_uv > fv_uv + JEITA_SIX_PIN_BATT_HYST_UV) { + if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER && fv_uv == WARM_VFLOAT_UV) + vote(chip->usb_icl_votable, JEITA_VOTER, true, 0); + } else if (curr_vbat_uv < (fv_uv - JEITA_SUSPEND_HYST_UV)) { + vote(chip->usb_icl_votable, JEITA_VOTER, false, 0); + } + } else { + if (curr_vbat_uv > fv_uv) { + if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER && fv_uv == WARM_VFLOAT_UV) + vote(chip->usb_icl_votable, JEITA_VOTER, true, 0); + } else if (curr_vbat_uv < (fv_uv - JEITA_SUSPEND_HYST_UV)) { + vote(chip->usb_icl_votable, JEITA_VOTER, false, 0); + } + } + } +#else if (!rc && (pval.intval > fv_uv)) vote(chip->usb_icl_votable, JEITA_VOTER, true, 0); else if (pval.intval < (fv_uv - JEITA_SUSPEND_HYST_UV)) vote(chip->usb_icl_votable, JEITA_VOTER, false, 0); +#endif } set_jeita_fv: @@ -873,7 +1263,13 @@ int qcom_step_chg_init(struct device *dev, chip->step_chg_config = devm_kzalloc(dev, sizeof(struct step_chg_cfg), GFP_KERNEL); +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chip->cold_step_chg_config = devm_kzalloc(dev, + sizeof(struct cold_step_chg_cfg), GFP_KERNEL); + if (!chip->step_chg_config || !chip->cold_step_chg_config) +#else if (!chip->step_chg_config) +#endif return -ENOMEM; chip->step_chg_config->param.psy_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW; @@ -881,6 +1277,13 @@ int qcom_step_chg_init(struct device *dev, chip->step_chg_config->param.rise_hys = 100000; chip->step_chg_config->param.fall_hys = 100000; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chip->cold_step_chg_config->param.psy_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW; + chip->cold_step_chg_config->param.prop_name = "VBATT"; + chip->cold_step_chg_config->param.rise_hys = 100000; + chip->cold_step_chg_config->param.fall_hys = 100000; +#endif + chip->jeita_fcc_config = devm_kzalloc(dev, sizeof(struct jeita_fcc_cfg), GFP_KERNEL); chip->jeita_fv_config = devm_kzalloc(dev, @@ -890,12 +1293,22 @@ int qcom_step_chg_init(struct device *dev, chip->jeita_fcc_config->param.psy_prop = POWER_SUPPLY_PROP_TEMP; chip->jeita_fcc_config->param.prop_name = "BATT_TEMP"; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chip->jeita_fcc_config->param.rise_hys = 20; + chip->jeita_fcc_config->param.fall_hys = 20; +#else chip->jeita_fcc_config->param.rise_hys = 10; chip->jeita_fcc_config->param.fall_hys = 10; +#endif chip->jeita_fv_config->param.psy_prop = POWER_SUPPLY_PROP_TEMP; chip->jeita_fv_config->param.prop_name = "BATT_TEMP"; +#ifdef CONFIG_MACH_XIAOMI_SM8250 + chip->jeita_fv_config->param.rise_hys = 20; + chip->jeita_fv_config->param.fall_hys = 20; +#else chip->jeita_fv_config->param.rise_hys = 10; chip->jeita_fv_config->param.fall_hys = 10; +#endif INIT_DELAYED_WORK(&chip->status_change_work, status_change_work); INIT_DELAYED_WORK(&chip->get_config_work, get_config_work); diff --git a/drivers/power/supply/qcom/step-chg-jeita.h b/drivers/power/supply/qcom/step-chg-jeita.h index 138602a66e9f..bc21a6a65d58 100644 --- a/drivers/power/supply/qcom/step-chg-jeita.h +++ b/drivers/power/supply/qcom/step-chg-jeita.h @@ -6,7 +6,14 @@ #ifndef __STEP_CHG_H__ #define __STEP_CHG_H__ +#ifdef CONFIG_MACH_XIAOMI_SM8250 +#define MAX_STEP_CHG_ENTRIES 6 +#define MAX_COLD_STEP_CHG_ENTRIES 2 +#define BATT_COOL_THRESHOLD 150 +#define BATT_WARM_THRESHOLD 450 +#else #define MAX_STEP_CHG_ENTRIES 8 +#endif struct step_chg_jeita_param { u32 psy_prop; diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h index 7f3b376c691f..e2057dbb8dce 100644 --- a/include/linux/power_supply.h +++ b/include/linux/power_supply.h @@ -107,6 +107,10 @@ enum { POWER_SUPPLY_DP_DM_FORCE_9V = 14, POWER_SUPPLY_DP_DM_FORCE_12V = 15, POWER_SUPPLY_DP_DM_CONFIRMED_HVDCP3P5 = 16, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_DP_DM_RAISE_VOLT_DONE = 17, + POWER_SUPPLY_DP_DM_RAISE_VOLT_START = 18, +#endif }; enum { @@ -208,6 +212,13 @@ enum power_supply_property { POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN, /* in percents! */ POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX, /* in percents! */ POWER_SUPPLY_PROP_CAPACITY_LEVEL, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_SHUTDOWN_DELAY, + POWER_SUPPLY_PROP_SHUTDOWN_DELAY_ENABLE, + POWER_SUPPLY_PROP_SOC_DECIMAL, + POWER_SUPPLY_PROP_SOC_DECIMAL_RATE, + POWER_SUPPLY_PROP_COLD_THERMAL_LEVEL, +#endif POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TEMP_MAX, POWER_SUPPLY_PROP_TEMP_MIN, @@ -232,6 +243,12 @@ enum power_supply_property { POWER_SUPPLY_PROP_CHARGE_ENABLED, POWER_SUPPLY_PROP_SET_SHIP_MODE, POWER_SUPPLY_PROP_REAL_TYPE, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_HVDCP3_TYPE, + POWER_SUPPLY_PROP_FAKE_HVDCP3, + POWER_SUPPLY_PROP_QUICK_CHARGE_TYPE, + POWER_SUPPLY_PROP_QUICK_CHARGE_POWER, +#endif POWER_SUPPLY_PROP_CHARGE_NOW_RAW, POWER_SUPPLY_PROP_CHARGE_NOW_ERROR, POWER_SUPPLY_PROP_CAPACITY_RAW, @@ -241,7 +258,14 @@ enum power_supply_property { POWER_SUPPLY_PROP_STEP_CHARGING_STEP, POWER_SUPPLY_PROP_PIN_ENABLED, POWER_SUPPLY_PROP_INPUT_SUSPEND, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_BATTERY_INPUT_SUSPEND, +#endif POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_INPUT_VOLTAGE_VRECT, + POWER_SUPPLY_PROP_RX_IOUT, +#endif POWER_SUPPLY_PROP_INPUT_CURRENT_MAX, POWER_SUPPLY_PROP_INPUT_CURRENT_TRIM, POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, @@ -266,6 +290,11 @@ enum power_supply_property { POWER_SUPPLY_PROP_BOOST_CURRENT, POWER_SUPPLY_PROP_SAFETY_TIMER_ENABLE, POWER_SUPPLY_PROP_CHARGE_DONE, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_HIZ_MODE, + POWER_SUPPLY_PROP_USB_CURRENT_NOW, + POWER_SUPPLY_PROP_USB_VOLTAGE_NOW, +#endif POWER_SUPPLY_PROP_FLASH_ACTIVE, POWER_SUPPLY_PROP_FLASH_TRIGGER, POWER_SUPPLY_PROP_FORCE_TLIM, @@ -283,11 +312,17 @@ enum power_supply_property { POWER_SUPPLY_PROP_TYPEC_MODE, POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION, /* 0: N/C, 1: CC1, 2: CC2 */ POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_TYPEC_BOOST_OTG_DISABLE, +#endif POWER_SUPPLY_PROP_TYPEC_SRC_RP, POWER_SUPPLY_PROP_PD_ALLOWED, POWER_SUPPLY_PROP_PD_ACTIVE, POWER_SUPPLY_PROP_PD_IN_HARD_RESET, POWER_SUPPLY_PROP_PD_CURRENT_MAX, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_APDO_MAX, +#endif POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED, POWER_SUPPLY_PROP_CHARGER_TEMP, POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, @@ -300,6 +335,11 @@ enum power_supply_property { POWER_SUPPLY_PROP_PARALLEL_MODE, POWER_SUPPLY_PROP_DIE_HEALTH, POWER_SUPPLY_PROP_CONNECTOR_HEALTH, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_CONNECTOR_TEMP, + POWER_SUPPLY_PROP_VBUS_DISABLE, + POWER_SUPPLY_PROP_ARTI_VBUS_ENABLE, +#endif POWER_SUPPLY_PROP_CTM_CURRENT_MAX, POWER_SUPPLY_PROP_HW_CURRENT_MAX, POWER_SUPPLY_PROP_PR_SWAP, @@ -309,13 +349,34 @@ enum power_supply_property { POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, POWER_SUPPLY_PROP_PD_VOLTAGE_MIN, POWER_SUPPLY_PROP_SDP_CURRENT_MAX, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_DC_THERMAL_LEVELS, +#endif POWER_SUPPLY_PROP_CONNECTOR_TYPE, POWER_SUPPLY_PROP_PARALLEL_BATFET_MODE, POWER_SUPPLY_PROP_PARALLEL_FCC_MAX, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_WIRELESS_VERSION, + POWER_SUPPLY_PROP_WIRELESS_FW_VERSION, + POWER_SUPPLY_PROP_SIGNAL_STRENGTH, + POWER_SUPPLY_PROP_WIRELESS_CP_EN, + POWER_SUPPLY_PROP_WIRELESS_POWER_GOOD_EN, + POWER_SUPPLY_PROP_SW_DISABLE_DC_EN, + POWER_SUPPLY_PROP_WIRELESS_WAKELOCK, + POWER_SUPPLY_PROP_WIRELESS_TX_ID, + POWER_SUPPLY_PROP_TX_ADAPTER, + POWER_SUPPLY_PROP_TX_MAC, + POWER_SUPPLY_PROP_RX_CR, + POWER_SUPPLY_PROP_RX_CEP, + POWER_SUPPLY_PROP_BT_STATE, +#endif POWER_SUPPLY_PROP_MIN_ICL, POWER_SUPPLY_PROP_MOISTURE_DETECTED, POWER_SUPPLY_PROP_BATT_PROFILE_VERSION, POWER_SUPPLY_PROP_BATT_FULL_CURRENT, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_WARM_FAKE_CHARGING, +#endif POWER_SUPPLY_PROP_RECHARGE_SOC, POWER_SUPPLY_PROP_HVDCP_OPTI_ALLOWED, POWER_SUPPLY_PROP_SMB_EN_MODE, @@ -326,7 +387,14 @@ enum power_supply_property { POWER_SUPPLY_PROP_CLEAR_SOH, POWER_SUPPLY_PROP_FORCE_RECHARGE, POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_SMB_EN_ALLOWED, + POWER_SUPPLY_PROP_BATT_2S_MODE, +#endif POWER_SUPPLY_PROP_TOGGLE_STAT, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_TYPE_RECHECK, +#endif POWER_SUPPLY_PROP_MAIN_FCC_MAX, POWER_SUPPLY_PROP_FG_RESET, POWER_SUPPLY_PROP_QC_OPTI_DISABLE, @@ -343,6 +411,9 @@ enum power_supply_property { POWER_SUPPLY_PROP_FORCE_MAIN_FCC, POWER_SUPPLY_PROP_COMP_CLAMP_LEVEL, POWER_SUPPLY_PROP_ADAPTER_CC_MODE, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_NON_COMPATIBLE, +#endif POWER_SUPPLY_PROP_SKIN_HEALTH, POWER_SUPPLY_PROP_AICL_DONE, POWER_SUPPLY_PROP_VOLTAGE_STEP, @@ -361,6 +432,68 @@ enum power_supply_property { POWER_SUPPLY_PROP_CP_ILIM, POWER_SUPPLY_PROP_IRQ_STATUS, POWER_SUPPLY_PROP_PARALLEL_OUTPUT_MODE, +#ifdef CONFIG_MACH_XIAOMI_SM8250 + POWER_SUPPLY_PROP_CP_WIN_OV, + POWER_SUPPLY_PROP_CP_PASSTHROUGH_MODE, + POWER_SUPPLY_PROP_CP_PASSTHROUGH_CONFIG, + POWER_SUPPLY_PROP_CP_OVP_CONFIG, + POWER_SUPPLY_PROP_CP_OCP_CONFIG, + POWER_SUPPLY_PROP_CP_CFLY_SS_STATUS, + POWER_SUPPLY_PROP_TI_BATTERY_PRESENT, + POWER_SUPPLY_PROP_TI_VBUS_PRESENT, + POWER_SUPPLY_PROP_TI_BATTERY_VOLTAGE, + POWER_SUPPLY_PROP_TI_BATTERY_CURRENT, + POWER_SUPPLY_PROP_TI_BATTERY_TEMPERATURE, + POWER_SUPPLY_PROP_TI_BUS_VOLTAGE, + POWER_SUPPLY_PROP_TI_BUS_CURRENT, + POWER_SUPPLY_PROP_TI_BUS_TEMPERATURE, + POWER_SUPPLY_PROP_TI_DIE_TEMPERATURE, + POWER_SUPPLY_PROP_TI_ALARM_STATUS, + POWER_SUPPLY_PROP_TI_FAULT_STATUS, + POWER_SUPPLY_PROP_TI_REG_STATUS, + POWER_SUPPLY_PROP_TI_SET_BUS_PROTECTION_FOR_QC3, + POWER_SUPPLY_PROP_TI_BUS_ERROR_STATUS, + POWER_SUPPLY_PROP_FASTCHARGE_MODE, + POWER_SUPPLY_PROP_DP_DM_BQ, + POWER_SUPPLY_PROP_PD_AUTHENTICATION, + POWER_SUPPLY_PROP_PASSTHROUGH_CURR_MAX, + POWER_SUPPLY_PROP_TERMINATION_CURRENT, + POWER_SUPPLY_PROP_FFC_TERMINATION_CURRENT, + POWER_SUPPLY_PROP_SYS_TERMINATION_CURRENT, + POWER_SUPPLY_PROP_FFC_SYS_TERMINATION_CURRENT, + POWER_SUPPLY_PROP_VBATT_FULL_VOL, + POWER_SUPPLY_PROP_FFC_VBATT_FULL_VOL, + POWER_SUPPLY_PROP_KI_COEFF_CURRENT, + POWER_SUPPLY_PROP_RECHARGE_VBAT, + POWER_SUPPLY_PROP_STEP_VFLOAT_INDEX, + POWER_SUPPLY_PROP_NIGHT_CHARGING, +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + /* battery verify properties */ + POWER_SUPPLY_PROP_ROMID, + POWER_SUPPLY_PROP_DS_STATUS, + POWER_SUPPLY_PROP_PAGENUMBER, + POWER_SUPPLY_PROP_PAGEDATA, + POWER_SUPPLY_PROP_AUTHEN_RESULT, + POWER_SUPPLY_PROP_SESSION_SEED, + POWER_SUPPLY_PROP_S_SECRET, + POWER_SUPPLY_PROP_CHALLENGE, + POWER_SUPPLY_PROP_AUTH_ANON, + POWER_SUPPLY_PROP_AUTH_BDCONST, + POWER_SUPPLY_PROP_PAGE0_DATA, + POWER_SUPPLY_PROP_PAGE1_DATA, + POWER_SUPPLY_PROP_VERIFY_MODEL_NAME, + POWER_SUPPLY_PROP_MAXIM_BATT_CYCLE_COUNT, +#endif + POWER_SUPPLY_PROP_CHIP_OK, + /* DIV 2 properties */ + POWER_SUPPLY_PROP_DIV_2_MODE, + POWER_SUPPLY_PROP_REVERSE_CHG_MODE, + POWER_SUPPLY_PROP_REVERSE_CHG_STATE, + POWER_SUPPLY_PROP_REVERSE_GPIO_STATE, + POWER_SUPPLY_PROP_RESET_DIV_2_MODE, + POWER_SUPPLY_PROP_AICL_ENABLE, + POWER_SUPPLY_PROP_OTG_STATE, +#endif POWER_SUPPLY_PROP_FG_TYPE, POWER_SUPPLY_PROP_CHARGER_STATUS, /* Local extensions of type int64_t */ @@ -402,6 +535,9 @@ 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 */ +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + POWER_SUPPLY_TYPE_BATT_VERIFY, /* battery verify */ +#endif }; enum power_supply_usb_type { @@ -456,6 +592,9 @@ union power_supply_propval { int intval; const char *strval; int64_t int64val; +#ifdef CONFIG_BATT_VERIFY_BY_DS28E16 + unsigned char arrayval[50]; +#endif }; struct device_node;