From 8583c761b5d387e7541e8743ece2d6ca0b63331f Mon Sep 17 00:00:00 2001 From: chailu1 Date: Fri, 4 Jun 2021 19:06:51 +0800 Subject: [PATCH] charge: add qc3p driver add qc3+ authenticate process and qqc3+ voltage control interface Change-Id: I1e944ba8d6b0d16c19c70f9d5c1385bbf9a28801 Signed-off-by: chailu1 Reviewed-on: https://gerrit.mot.com/1971082 SLTApproved: Slta Waiver SME-Granted: SME Approvals Granted Tested-by: Jira Key Reviewed-by: Haijian Ma Reviewed-by: Huosheng Liao Submit-Approved: Jira Key --- drivers/power/supply/qcom/qpnp-smb5.c | 2 + drivers/power/supply/qcom/smb5-iio.c | 10 +- drivers/power/supply/qcom/smb5-iio.h | 6 + drivers/power/supply/qcom/smb5-lib.c | 184 +++++++++++++++++- drivers/power/supply/qcom/smb5-lib.h | 10 + .../dt-bindings/iio/qti_power_supply_iio.h | 1 + include/linux/qti_power_supply.h | 6 + 7 files changed, 216 insertions(+), 3 deletions(-) diff --git a/drivers/power/supply/qcom/qpnp-smb5.c b/drivers/power/supply/qcom/qpnp-smb5.c index 01f96db6d6c7..6ef6655e5640 100644 --- a/drivers/power/supply/qcom/qpnp-smb5.c +++ b/drivers/power/supply/qcom/qpnp-smb5.c @@ -626,6 +626,8 @@ static int smb5_parse_dt_misc(struct smb5 *chip, struct device_node *node) &chg->chg_param.qc4_max_icl_ua); if (chg->chg_param.qc4_max_icl_ua <= 0) chg->chg_param.qc4_max_icl_ua = MICRO_4PA; + chg->mmi_qc3p_support = of_property_read_bool(node, + "mmi,qc3p-support"); return 0; } diff --git a/drivers/power/supply/qcom/smb5-iio.c b/drivers/power/supply/qcom/smb5-iio.c index 7a2f368b82dd..d34e93e1cdd0 100644 --- a/drivers/power/supply/qcom/smb5-iio.c +++ b/drivers/power/supply/qcom/smb5-iio.c @@ -269,6 +269,9 @@ int smb5_iio_get_prop(struct smb_charger *chg, int channel, int *val) case PSY_IIO_CHARGING_ENABLED: *val = !get_effective_result(chg->chg_disable_votable); break; + case PSY_IIO_MMI_QC3P_POWER: + *val = chg->mmi_qc3p_power; + break; default: pr_err("get prop %d is not supported\n", channel); rc = -EINVAL; @@ -471,7 +474,12 @@ int smb5_iio_set_prop(struct smb_charger *chg, int channel, int val) rc = smblib_run_aicl(chg, RERUN_AICL); break; case PSY_IIO_DP_DM: - if (!chg->flash_active) + if (chg->mmi_qc3p_support) { + if ((chg->real_charger_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5) + ||(!chg->flash_active && !chg->mmi_is_qc3p_authen + && chg->real_charger_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3)) + rc = smblib_dp_dm(chg, val); + } else if (!chg->flash_active) rc = smblib_dp_dm(chg, val); break; case PSY_IIO_INPUT_CURRENT_LIMITED: diff --git a/drivers/power/supply/qcom/smb5-iio.h b/drivers/power/supply/qcom/smb5-iio.h index 800deefe37d0..e5ecb0308864 100644 --- a/drivers/power/supply/qcom/smb5-iio.h +++ b/drivers/power/supply/qcom/smb5-iio.h @@ -14,6 +14,7 @@ enum iio_type { QG, CP, SMB_PARALLEL, + MMI_CP, }; /* For qpnp-smb5.c and smb5-lib.c */ @@ -34,6 +35,10 @@ enum qg_chg_iio_channels { SMB5_QG_TIME_TO_FULL_NOW, }; +enum mmi_cp_iio_channels { + SC8549_INPUT_VOLTAGE_NOW, +}; + enum cp_iio_channels { CP_PARALLEL_OUTPUT_MODE, CP_MASTER_ENABLE, @@ -202,6 +207,7 @@ static const struct smb5_iio_prop_channels smb5_chans_pmic[] = { SMB5_CHAN_ACTIVITY("battery_fcc_stepper_enable", FCC_STEPPER_ENABLE) SMB5_CHAN_INDEX("usb_typec_accessory_mode", TYPEC_ACCESSORY_MODE) SMB5_CHAN_INDEX("charging_enabled", CHARGING_ENABLED) + SMB5_CHAN_INDEX("usb_qc3p_power", MMI_QC3P_POWER) }; struct iio_channel **get_ext_channels(struct device *dev, diff --git a/drivers/power/supply/qcom/smb5-lib.c b/drivers/power/supply/qcom/smb5-lib.c index 38cb13bb4cb6..f0c2491079e3 100644 --- a/drivers/power/supply/qcom/smb5-lib.c +++ b/drivers/power/supply/qcom/smb5-lib.c @@ -24,6 +24,9 @@ #include "storm-watch.h" #include "smb5-iio.h" #include "battery-profile-loader.h" +#include +#include + #define smblib_err(chg, fmt, ...) \ pr_err("%s: %s: " fmt, chg->name, \ @@ -885,6 +888,10 @@ int smblib_set_aicl_cont_threshold(struct smb_chg_param *param, /******************** * HELPER FUNCTIONS * ********************/ +/* MMI CP channels */ +static const char * const smblib_mmi_cp_ext_iio_chan[] = { + [SC8549_INPUT_VOLTAGE_NOW] = "sc8549_input_voltage_now", +}; /* CP channels */ static const char * const smblib_cp_ext_iio_chan[] = { @@ -941,6 +948,11 @@ static int smblib_read_iio_prop(struct smb_charger *chg, return -ENODEV; iio_chan_list = chg->iio_chan_list_smb_parallel[iio_chan]; break; + case MMI_CP: + if (IS_ERR_OR_NULL(chg->iio_chan_list_mmi_cp)) + return -ENODEV; + iio_chan_list = chg->iio_chan_list_mmi_cp[iio_chan]; + break; default: pr_err_ratelimited("iio_type %d is not supported\n", type); return -EINVAL; @@ -971,6 +983,11 @@ static int smblib_write_iio_prop(struct smb_charger *chg, return -ENODEV; iio_chan_list = chg->iio_chan_list_smb_parallel[iio_chan]; break; + case MMI_CP: + if (IS_ERR_OR_NULL(chg->iio_chan_list_mmi_cp)) + return -ENODEV; + iio_chan_list = chg->iio_chan_list_mmi_cp[iio_chan]; + break; default: pr_err_ratelimited("iio_type %d is not supported\n", type); return -EINVAL; @@ -979,6 +996,33 @@ static int smblib_write_iio_prop(struct smb_charger *chg, return iio_write_channel_raw(iio_chan_list, val); } +static bool is_mmi_cp_available(struct smb_charger *chg) +{ + int rc; + struct iio_channel **iio_list; + + if (IS_ERR(chg->iio_chan_list_mmi_cp)) + return false; + + if (!chg->iio_chan_list_mmi_cp) { + iio_list = get_ext_channels(chg->dev, + smblib_mmi_cp_ext_iio_chan, + ARRAY_SIZE(smblib_mmi_cp_ext_iio_chan)); + if (IS_ERR(iio_list)) { + rc = PTR_ERR(iio_list); + if (rc != -EPROBE_DEFER) { + dev_err(chg->dev, "Failed to get channels, rc=%d\n", + rc); + chg->iio_chan_list_mmi_cp = ERR_PTR(-EINVAL); + } + return false; + } + chg->iio_chan_list_mmi_cp = iio_list; + } + + return true; +} + static bool is_cp_available(struct smb_charger *chg) { int rc; @@ -1305,6 +1349,7 @@ static void smblib_uusb_removal(struct smb_charger *chg) vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, is_flash_active(chg) ? SDP_CURRENT_UA : SDP_100_MA); vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0); + vote(chg->usb_icl_votable, SW_QC3P_AUTHEN_VOTER, false, 0); 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); @@ -2869,8 +2914,10 @@ int smblib_dp_dm(struct smb_charger *chg, int val) pr_err("Failed to force 12V\n"); break; case QTI_POWER_SUPPLY_DP_DM_CONFIRMED_HVDCP3P5: - chg->qc3p5_detected = true; - smblib_update_usb_type(chg); + if (!chg->mmi_qc3p_support) { + chg->qc3p5_detected = true; + smblib_update_usb_type(chg); + } break; case QTI_POWER_SUPPLY_DP_DM_ICL_UP: default: @@ -5862,6 +5909,113 @@ static void smblib_handle_sdp_enumeration_done(struct smb_charger *chg, rising ? "rising" : "falling"); } +static int mmi_get_vbus(struct smb_charger *chg) +{ + int rc = 0, val; + + if (!is_mmi_cp_available(chg)) + return 0; + + rc = smblib_read_iio_prop(chg, MMI_CP, SC8549_INPUT_VOLTAGE_NOW, + &val); + if (!rc) + return val; + else { + smblib_err(chg, "Couldn't read mmi cp vbus rc=%d\n", rc); + return 0; + } +} + +#define QC3P_AUTHEN_LOW_THR_MV 5500 +#define QC3P_AUTHEN_HIGH_THR_MV 6500 +#define QC3P_AUTHEN_NONE_THR_MV 9500 +#define QC3P_AUTHEN_45W_THR_MV 8500 +#define QC3P_AUTHEN_27W_THR_MV 7500 +#define QC3P_AUTHEN_18W_THR_MV 6500 +static int mmi_qc3p_kthread_handler(void *param) +{ + struct smb_charger *chg = param; + int i; + int v_aut,v_power; + unsigned long j1,j2; + struct sched_param sch_param = {.sched_priority = MAX_RT_PRIO-1}; + + sched_setscheduler(current, SCHED_FIFO, &sch_param); + do { + wait_event_interruptible(chg->mmi_timer_wait_que, chg->mmi_timer_trig_flag || kthread_should_stop()); + if (kthread_should_stop()) + break; + chg->mmi_timer_trig_flag = false; + + for (i = 0; i < 16; i++) { + smblib_dp_pulse(chg); + udelay(5000); + } + mdelay(95); + for (i = 0; i < 16; i++) { + smblib_dm_pulse(chg); + udelay(5000); + } + mdelay(100); + + j1 = jiffies; + v_aut = mmi_get_vbus(chg); + j1 = jiffies - j1; + if (v_aut < QC3P_AUTHEN_LOW_THR_MV || v_aut > QC3P_AUTHEN_HIGH_THR_MV) { + chg->mmi_qc3p_power = QTI_POWER_SUPPLY_QC3P_NONE; + smblib_err(chg, "is qc3.0 charge adatper vbus=%d\n",v_aut); + goto out; + } + + for (i = 0;i < 3; i++) { + smblib_dp_pulse(chg); + udelay(5000); + smblib_dm_pulse(chg); + udelay(5000); + } + mdelay(30); + + j2 = jiffies; + v_power = mmi_get_vbus(chg); + j2 = jiffies - j2; + + for (i = 0; i < 2; i++) { + smblib_dp_pulse(chg); + udelay(5000); + } + for (i = 0; i < 2; i++) { + smblib_dm_pulse(chg); + udelay(5000); + } + msleep(30); + + if (v_power > QC3P_AUTHEN_NONE_THR_MV) + chg->mmi_qc3p_power = QTI_POWER_SUPPLY_QC3P_NONE; + else if (v_power > QC3P_AUTHEN_45W_THR_MV) + chg->mmi_qc3p_power = QTI_POWER_SUPPLY_QC3P_45W; + else if (v_power > QC3P_AUTHEN_27W_THR_MV) + chg->mmi_qc3p_power = QTI_POWER_SUPPLY_QC3P_27W; + else if (v_power > QC3P_AUTHEN_18W_THR_MV) + chg->mmi_qc3p_power = QTI_POWER_SUPPLY_QC3P_18W; + else + chg->mmi_qc3p_power = QTI_POWER_SUPPLY_QC3P_NONE; + + if (chg->mmi_qc3p_power != QTI_POWER_SUPPLY_QC3P_NONE) + chg->qc3p5_detected = true; + smblib_update_usb_type(chg); + power_supply_changed(chg->usb_psy); + + smblib_dbg(chg, PR_MISC, "V_aut:%dms %dmV,V_power=%dms %dmV,Power =%d\n", + jiffies_to_msecs(j1), v_aut, jiffies_to_msecs(j2), v_power, chg->mmi_qc3p_power); + out: + chg->mmi_is_qc3p_authen = false; + vote(chg->usb_icl_votable, SW_QC3P_AUTHEN_VOTER, false, 0); + }while (!kthread_should_stop()); + + smblib_dbg(chg, PR_MISC, "qc3p kthread stop\n"); + return 0; +} + #define APSD_EXTENDED_TIMEOUT_MS 400 /* triggers when HVDCP 3.0 authentication has finished */ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg, @@ -5898,6 +6052,13 @@ static void smblib_handle_hvdcp_3p0_auth_done(struct smb_charger *chg, "APSD Extented timer started at %lld\n", jiffies_to_msecs(jiffies)); + if (chg->mmi_qc3p_support) { + chg->mmi_is_qc3p_authen = true; + chg->qc3p5_detected = false; + chg->mmi_qc3p_power = QTI_POWER_SUPPLY_QC3P_NONE; + vote(chg->usb_icl_votable, SW_QC3P_AUTHEN_VOTER, true, 500000); + } + mod_timer(&chg->apsd_timer, msecs_to_jiffies(APSD_EXTENDED_TIMEOUT_MS) + jiffies); @@ -6477,6 +6638,7 @@ static void typec_src_removal(struct smb_charger *chg) vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); vote(chg->usb_icl_votable, DCP_VOTER, false, 0); vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0); + vote(chg->usb_icl_votable, SW_QC3P_AUTHEN_VOTER, false, 0); vote(chg->usb_icl_votable, CTM_VOTER, false, 0); vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER, false, 0); vote(chg->usb_icl_votable, CHG_TERMINATION_VOTER, false, 0); @@ -7985,6 +8147,11 @@ static void apsd_timer_cb(struct timer_list *tm) jiffies_to_msecs(jiffies)); chg->apsd_ext_timeout = true; + + if (chg->mmi_qc3p_support) { + chg->mmi_timer_trig_flag = true; + wake_up_interruptible(&chg->mmi_timer_wait_que); + } } #define SOFT_JEITA_HYSTERESIS_OFFSET 0x200 @@ -8559,6 +8726,17 @@ int smblib_init(struct smb_charger *chg) INIT_DELAYED_WORK(&chg->pr_lock_clear_work, smblib_pr_lock_clear_work); INIT_DELAYED_WORK(&chg->pd_contract_work, smblib_pd_contract_work); + if (chg->mmi_qc3p_support) { + chg->mmi_qc3p_authen_task = kthread_create(mmi_qc3p_kthread_handler, chg, + "mmi_qc3p_authen", "mmi_qc3p_authen_task", chg); + if (IS_ERR(chg->mmi_qc3p_authen_task)) { + rc = PTR_ERR(chg->mmi_qc3p_authen_task); + smblib_err(chg, "Failed to create mmi_qc3p_authen_task rc = %d\n", rc); + return rc; + } + init_waitqueue_head(&chg->mmi_timer_wait_que); + wake_up_process(chg->mmi_qc3p_authen_task); + } timer_setup(&chg->apsd_timer, apsd_timer_cb, 0); INIT_DELAYED_WORK(&chg->role_reversal_check, @@ -8729,6 +8907,8 @@ int smblib_deinit(struct smb_charger *chg) cancel_delayed_work_sync(&chg->usbov_dbc_work); cancel_delayed_work_sync(&chg->role_reversal_check); cancel_delayed_work_sync(&chg->pr_swap_detach_work); + if (chg->mmi_qc3p_support && chg->mmi_qc3p_authen_task) + kthread_stop(chg->mmi_qc3p_authen_task); power_supply_unreg_notifier(&chg->nb); smblib_destroy_votables(chg); qcom_step_chg_deinit(); diff --git a/drivers/power/supply/qcom/smb5-lib.h b/drivers/power/supply/qcom/smb5-lib.h index 2dea1c0aff4b..75cad217ee77 100644 --- a/drivers/power/supply/qcom/smb5-lib.h +++ b/drivers/power/supply/qcom/smb5-lib.h @@ -84,6 +84,7 @@ enum print_reason { #define ICL_CHANGE_VOTER "ICL_CHANGE_VOTER" #define OVERHEAT_LIMIT_VOTER "OVERHEAT_LIMIT_VOTER" #define TYPEC_SWAP_VOTER "TYPEC_SWAP_VOTER" +#define SW_QC3P_AUTHEN_VOTER "SW_QC3P_AUTHEN_VOTER" #define BOOST_BACK_STORM_COUNT 3 #define WEAK_CHG_STORM_COUNT 8 @@ -639,6 +640,15 @@ struct smb_charger { ktime_t dcin_uv_last_time; int last_wls_vout; int usb_dcp_curr_max; + + /* mmi qc3p */ + struct iio_channel **iio_chan_list_mmi_cp; + struct task_struct *mmi_qc3p_authen_task; + wait_queue_head_t mmi_timer_wait_que; + enum mmi_qc3p_power mmi_qc3p_power; + bool mmi_is_qc3p_authen; + bool mmi_qc3p_support; + bool mmi_timer_trig_flag; }; int smblib_read(struct smb_charger *chg, u16 addr, u8 *val); diff --git a/include/dt-bindings/iio/qti_power_supply_iio.h b/include/dt-bindings/iio/qti_power_supply_iio.h index d4bd059af5f5..5bf925213ead 100644 --- a/include/dt-bindings/iio/qti_power_supply_iio.h +++ b/include/dt-bindings/iio/qti_power_supply_iio.h @@ -160,4 +160,5 @@ #define PSY_IIO_MMI_CP_INPUT_VOLTAGE_NOW 0x101 #define PSY_IIO_MMI_CP_STATUS1 0x102 #define PSY_IIO_MMI_CP_CLEAR_ERROR 0x103 +#define PSY_IIO_MMI_QC3P_POWER 0x104 #endif /* __QTI_POWER_SUPPLY_IIO_H__ */ diff --git a/include/linux/qti_power_supply.h b/include/linux/qti_power_supply.h index 8b5386389c3d..83c573e570c4 100644 --- a/include/linux/qti_power_supply.h +++ b/include/linux/qti_power_supply.h @@ -113,4 +113,10 @@ enum power_supply_typec_power_role { QTI_POWER_SUPPLY_TYPEC_PR_SOURCE, }; +enum mmi_qc3p_power { + QTI_POWER_SUPPLY_QC3P_NONE, + QTI_POWER_SUPPLY_QC3P_18W, + QTI_POWER_SUPPLY_QC3P_27W, + QTI_POWER_SUPPLY_QC3P_45W, +}; #endif /* __QTI_POWER_SUPPLY_H_ */