103e5d38eSCan Guo // SPDX-License-Identifier: GPL-2.0-only 203e5d38eSCan Guo /* 303e5d38eSCan Guo * Copyright (C) 2026 Qualcomm Technologies, Inc. 403e5d38eSCan Guo * 503e5d38eSCan Guo * Author: 603e5d38eSCan Guo * Can Guo <can.guo@oss.qualcomm.com> 703e5d38eSCan Guo */ 803e5d38eSCan Guo 903e5d38eSCan Guo #include <linux/bitops.h> 1003e5d38eSCan Guo #include <linux/delay.h> 1103e5d38eSCan Guo #include <linux/errno.h> 1203e5d38eSCan Guo #include <linux/kernel.h> 1303e5d38eSCan Guo #include <ufs/ufshcd.h> 1403e5d38eSCan Guo #include <ufs/unipro.h> 1503e5d38eSCan Guo #include "ufshcd-priv.h" 1603e5d38eSCan Guo 1703e5d38eSCan Guo static bool use_adaptive_txeq; 1803e5d38eSCan Guo module_param(use_adaptive_txeq, bool, 0644); 1903e5d38eSCan Guo MODULE_PARM_DESC(use_adaptive_txeq, "Find and apply optimal TX Equalization settings before changing Power Mode (default: false)"); 2003e5d38eSCan Guo 2103e5d38eSCan Guo static int txeq_gear_set(const char *val, const struct kernel_param *kp) 2203e5d38eSCan Guo { 2303e5d38eSCan Guo return param_set_uint_minmax(val, kp, UFS_HS_G1, UFS_HS_GEAR_MAX); 2403e5d38eSCan Guo } 2503e5d38eSCan Guo 2603e5d38eSCan Guo static const struct kernel_param_ops txeq_gear_ops = { 2703e5d38eSCan Guo .set = txeq_gear_set, 2803e5d38eSCan Guo .get = param_get_uint, 2903e5d38eSCan Guo }; 3003e5d38eSCan Guo 3103e5d38eSCan Guo static unsigned int adaptive_txeq_gear = UFS_HS_G6; 3203e5d38eSCan Guo module_param_cb(adaptive_txeq_gear, &txeq_gear_ops, &adaptive_txeq_gear, 0644); 3303e5d38eSCan Guo MODULE_PARM_DESC(adaptive_txeq_gear, "For HS-Gear[n] and above, adaptive txeq shall be used"); 3403e5d38eSCan Guo 3503e5d38eSCan Guo static bool use_txeq_presets; 3603e5d38eSCan Guo module_param(use_txeq_presets, bool, 0644); 3703e5d38eSCan Guo MODULE_PARM_DESC(use_txeq_presets, "Use only the 8 TX Equalization Presets (pre-defined Pre-Shoot & De-Emphasis combinations) for TX EQTR (default: false)"); 3803e5d38eSCan Guo 3903e5d38eSCan Guo static bool txeq_presets_selected[UFS_TX_EQ_PRESET_MAX] = {[0 ... (UFS_TX_EQ_PRESET_MAX - 1)] = 1}; 4003e5d38eSCan Guo module_param_array(txeq_presets_selected, bool, NULL, 0644); 4103e5d38eSCan Guo MODULE_PARM_DESC(txeq_presets_selected, "Use only the selected Presets out of the 8 TX Equalization Presets for TX EQTR"); 4203e5d38eSCan Guo 4303e5d38eSCan Guo /* 4403e5d38eSCan Guo * ufs_tx_eq_preset - Table of minimum required list of presets. 4503e5d38eSCan Guo * 4603e5d38eSCan Guo * A HS-G6 capable M-TX shall support the presets defined in M-PHY v6.0 spec. 4703e5d38eSCan Guo * Preset Pre-Shoot(dB) De-Emphasis(dB) 4803e5d38eSCan Guo * P0 0.0 0.0 4903e5d38eSCan Guo * P1 0.0 0.8 5003e5d38eSCan Guo * P2 0.0 1.6 5103e5d38eSCan Guo * P3 0.8 0.0 5203e5d38eSCan Guo * P4 1.6 0.0 5303e5d38eSCan Guo * P5 0.8 0.8 5403e5d38eSCan Guo * P6 0.8 1.6 5503e5d38eSCan Guo * P7 1.6 0.8 5603e5d38eSCan Guo */ 5703e5d38eSCan Guo static const struct __ufs_tx_eq_preset { 5803e5d38eSCan Guo u8 preshoot; 5903e5d38eSCan Guo u8 deemphasis; 6003e5d38eSCan Guo } ufs_tx_eq_preset[UFS_TX_EQ_PRESET_MAX] = { 6103e5d38eSCan Guo [UFS_TX_EQ_PRESET_P0] = {UFS_TX_HS_PRESHOOT_DB_0P0, UFS_TX_HS_DEEMPHASIS_DB_0P0}, 6203e5d38eSCan Guo [UFS_TX_EQ_PRESET_P1] = {UFS_TX_HS_PRESHOOT_DB_0P0, UFS_TX_HS_DEEMPHASIS_DB_0P8}, 6303e5d38eSCan Guo [UFS_TX_EQ_PRESET_P2] = {UFS_TX_HS_PRESHOOT_DB_0P0, UFS_TX_HS_DEEMPHASIS_DB_1P6}, 6403e5d38eSCan Guo [UFS_TX_EQ_PRESET_P3] = {UFS_TX_HS_PRESHOOT_DB_0P8, UFS_TX_HS_DEEMPHASIS_DB_0P0}, 6503e5d38eSCan Guo [UFS_TX_EQ_PRESET_P4] = {UFS_TX_HS_PRESHOOT_DB_1P6, UFS_TX_HS_DEEMPHASIS_DB_0P0}, 6603e5d38eSCan Guo [UFS_TX_EQ_PRESET_P5] = {UFS_TX_HS_PRESHOOT_DB_0P8, UFS_TX_HS_DEEMPHASIS_DB_0P8}, 6703e5d38eSCan Guo [UFS_TX_EQ_PRESET_P6] = {UFS_TX_HS_PRESHOOT_DB_0P8, UFS_TX_HS_DEEMPHASIS_DB_1P6}, 6803e5d38eSCan Guo [UFS_TX_EQ_PRESET_P7] = {UFS_TX_HS_PRESHOOT_DB_1P6, UFS_TX_HS_DEEMPHASIS_DB_0P8}, 6903e5d38eSCan Guo }; 7003e5d38eSCan Guo 7103e5d38eSCan Guo /* 7203e5d38eSCan Guo * pa_peer_rx_adapt_initial - Table of UniPro PA_PeerRxHSGnAdaptInitial 7303e5d38eSCan Guo * attribute IDs for High Speed (HS) Gears. 7403e5d38eSCan Guo * 7503e5d38eSCan Guo * This table maps HS Gears to their respective UniPro PA_PeerRxHSGnAdaptInitial 7603e5d38eSCan Guo * attribute IDs. Entries for Gears 1-3 are 0 (unsupported). 7703e5d38eSCan Guo */ 7803e5d38eSCan Guo static const u32 pa_peer_rx_adapt_initial[UFS_HS_GEAR_MAX] = { 7903e5d38eSCan Guo 0, 8003e5d38eSCan Guo 0, 8103e5d38eSCan Guo 0, 8203e5d38eSCan Guo PA_PEERRXHSG4ADAPTINITIAL, 8303e5d38eSCan Guo PA_PEERRXHSG5ADAPTINITIAL, 8403e5d38eSCan Guo PA_PEERRXHSG6ADAPTINITIALL0L3 8503e5d38eSCan Guo }; 8603e5d38eSCan Guo 8703e5d38eSCan Guo /* 8803e5d38eSCan Guo * rx_adapt_initial_cap - Table of M-PHY RX_HS_Gn_ADAPT_INITIAL_Capability 8903e5d38eSCan Guo * attribute IDs for High Speed (HS) Gears. 9003e5d38eSCan Guo * 9103e5d38eSCan Guo * This table maps HS Gears to their respective M-PHY 9203e5d38eSCan Guo * RX_HS_Gn_ADAPT_INITIAL_Capability attribute IDs. Entries for Gears 1-3 are 0 9303e5d38eSCan Guo * (unsupported). 9403e5d38eSCan Guo */ 9503e5d38eSCan Guo static const u32 rx_adapt_initial_cap[UFS_HS_GEAR_MAX] = { 9603e5d38eSCan Guo 0, 9703e5d38eSCan Guo 0, 9803e5d38eSCan Guo 0, 9903e5d38eSCan Guo RX_HS_G4_ADAPT_INITIAL_CAP, 10003e5d38eSCan Guo RX_HS_G5_ADAPT_INITIAL_CAP, 10103e5d38eSCan Guo RX_HS_G6_ADAPT_INITIAL_CAP 10203e5d38eSCan Guo }; 10303e5d38eSCan Guo 10403e5d38eSCan Guo /* 10503e5d38eSCan Guo * pa_tx_eq_setting - Table of UniPro PA_TxEQGnSetting attribute IDs for High 10603e5d38eSCan Guo * Speed (HS) Gears. 10703e5d38eSCan Guo * 10803e5d38eSCan Guo * This table maps HS Gears to their respective UniPro PA_TxEQGnSetting 10903e5d38eSCan Guo * attribute IDs. 11003e5d38eSCan Guo */ 11103e5d38eSCan Guo static const u32 pa_tx_eq_setting[UFS_HS_GEAR_MAX] = { 11203e5d38eSCan Guo PA_TXEQG1SETTING, 11303e5d38eSCan Guo PA_TXEQG2SETTING, 11403e5d38eSCan Guo PA_TXEQG3SETTING, 11503e5d38eSCan Guo PA_TXEQG4SETTING, 11603e5d38eSCan Guo PA_TXEQG5SETTING, 11703e5d38eSCan Guo PA_TXEQG6SETTING 11803e5d38eSCan Guo }; 11903e5d38eSCan Guo 12003e5d38eSCan Guo /** 12103e5d38eSCan Guo * ufshcd_configure_precoding - Configure Pre-Coding for all active lanes 12203e5d38eSCan Guo * @hba: per adapter instance 12303e5d38eSCan Guo * @params: TX EQ parameters data structure 12403e5d38eSCan Guo * 12503e5d38eSCan Guo * Bit[7] in RX_FOM indicates that the receiver needs to enable Pre-Coding when 12603e5d38eSCan Guo * set. Pre-Coding must be enabled on both the transmitter and receiver to 12703e5d38eSCan Guo * ensure proper operation. 12803e5d38eSCan Guo * 12903e5d38eSCan Guo * Returns 0 on success, non-zero error code otherwise 13003e5d38eSCan Guo */ 13103e5d38eSCan Guo static int ufshcd_configure_precoding(struct ufs_hba *hba, 13203e5d38eSCan Guo struct ufshcd_tx_eq_params *params) 13303e5d38eSCan Guo { 13403e5d38eSCan Guo struct ufs_pa_layer_attr *pwr_info = &hba->max_pwr_info.info; 13503e5d38eSCan Guo u32 local_precode_en = 0; 13603e5d38eSCan Guo u32 peer_precode_en = 0; 13703e5d38eSCan Guo int lane, ret; 13803e5d38eSCan Guo 13903e5d38eSCan Guo /* Enable Pre-Coding for Host's TX & Device's RX pair */ 14003e5d38eSCan Guo for (lane = 0; lane < pwr_info->lane_tx; lane++) { 14103e5d38eSCan Guo if (params->host[lane].precode_en) { 14203e5d38eSCan Guo local_precode_en |= PRECODEEN_TX_BIT(lane); 14303e5d38eSCan Guo peer_precode_en |= PRECODEEN_RX_BIT(lane); 14403e5d38eSCan Guo } 14503e5d38eSCan Guo } 14603e5d38eSCan Guo 14703e5d38eSCan Guo /* Enable Pre-Coding for Device's TX & Host's RX pair */ 14803e5d38eSCan Guo for (lane = 0; lane < pwr_info->lane_rx; lane++) { 14903e5d38eSCan Guo if (params->device[lane].precode_en) { 15003e5d38eSCan Guo peer_precode_en |= PRECODEEN_TX_BIT(lane); 15103e5d38eSCan Guo local_precode_en |= PRECODEEN_RX_BIT(lane); 15203e5d38eSCan Guo } 15303e5d38eSCan Guo } 15403e5d38eSCan Guo 15503e5d38eSCan Guo if (!local_precode_en && !peer_precode_en) { 15603e5d38eSCan Guo dev_dbg(hba->dev, "Pre-Coding is not required for Host and Device\n"); 15703e5d38eSCan Guo return 0; 15803e5d38eSCan Guo } 15903e5d38eSCan Guo 16003e5d38eSCan Guo /* Set local PA_PreCodeEn */ 16103e5d38eSCan Guo ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PRECODEEN), local_precode_en); 16203e5d38eSCan Guo if (ret) { 16303e5d38eSCan Guo dev_err(hba->dev, "Failed to set local PA_PreCodeEn: %d\n", ret); 16403e5d38eSCan Guo return ret; 16503e5d38eSCan Guo } 16603e5d38eSCan Guo 16703e5d38eSCan Guo /* Set peer PA_PreCodeEn */ 16803e5d38eSCan Guo ret = ufshcd_dme_peer_set(hba, UIC_ARG_MIB(PA_PRECODEEN), peer_precode_en); 16903e5d38eSCan Guo if (ret) { 17003e5d38eSCan Guo dev_err(hba->dev, "Failed to set peer PA_PreCodeEn: %d\n", ret); 17103e5d38eSCan Guo return ret; 17203e5d38eSCan Guo } 17303e5d38eSCan Guo 17403e5d38eSCan Guo dev_dbg(hba->dev, "Local PA_PreCodeEn: 0x%02x, Peer PA_PreCodeEn: 0x%02x\n", 17503e5d38eSCan Guo local_precode_en, peer_precode_en); 17603e5d38eSCan Guo 17703e5d38eSCan Guo return 0; 17803e5d38eSCan Guo } 17903e5d38eSCan Guo 18003e5d38eSCan Guo void ufshcd_print_tx_eq_params(struct ufs_hba *hba) 18103e5d38eSCan Guo { 18203e5d38eSCan Guo struct ufs_pa_layer_attr *pwr_info = &hba->max_pwr_info.info; 18303e5d38eSCan Guo struct ufshcd_tx_eq_params *params; 18403e5d38eSCan Guo u32 gear = hba->pwr_info.gear_tx; 18503e5d38eSCan Guo int lane; 18603e5d38eSCan Guo 18703e5d38eSCan Guo if (!ufshcd_is_tx_eq_supported(hba)) 18803e5d38eSCan Guo return; 18903e5d38eSCan Guo 19003e5d38eSCan Guo if (gear < UFS_HS_G1 || gear > UFS_HS_GEAR_MAX) 19103e5d38eSCan Guo return; 19203e5d38eSCan Guo 19303e5d38eSCan Guo params = &hba->tx_eq_params[gear - 1]; 19403e5d38eSCan Guo if (!params->is_valid || !params->is_applied) 19503e5d38eSCan Guo return; 19603e5d38eSCan Guo 19703e5d38eSCan Guo for (lane = 0; lane < pwr_info->lane_tx; lane++) 19803e5d38eSCan Guo dev_dbg(hba->dev, "Host TX Lane %d: PreShoot %u, DeEmphasis %u, FOM %u, PreCodeEn %d\n", 19903e5d38eSCan Guo lane, params->host[lane].preshoot, 20003e5d38eSCan Guo params->host[lane].deemphasis, 20103e5d38eSCan Guo params->host[lane].fom_val, 20203e5d38eSCan Guo params->host[lane].precode_en); 20303e5d38eSCan Guo 20403e5d38eSCan Guo for (lane = 0; lane < pwr_info->lane_rx; lane++) 20503e5d38eSCan Guo dev_dbg(hba->dev, "Device TX Lane %d: PreShoot %u, DeEmphasis %u, FOM %u, PreCodeEn %d\n", 20603e5d38eSCan Guo lane, params->device[lane].preshoot, 20703e5d38eSCan Guo params->device[lane].deemphasis, 20803e5d38eSCan Guo params->device[lane].fom_val, 20903e5d38eSCan Guo params->device[lane].precode_en); 21003e5d38eSCan Guo } 21103e5d38eSCan Guo 21203e5d38eSCan Guo static inline u32 21303e5d38eSCan Guo ufshcd_compose_tx_eq_setting(struct ufshcd_tx_eq_settings *settings, 21403e5d38eSCan Guo int num_lanes) 21503e5d38eSCan Guo { 21603e5d38eSCan Guo u32 setting = 0; 21703e5d38eSCan Guo int lane; 21803e5d38eSCan Guo 21903e5d38eSCan Guo for (lane = 0; lane < num_lanes; lane++, settings++) { 22003e5d38eSCan Guo setting |= TX_HS_PRESHOOT_BITS(lane, settings->preshoot); 22103e5d38eSCan Guo setting |= TX_HS_DEEMPHASIS_BITS(lane, settings->deemphasis); 22203e5d38eSCan Guo } 22303e5d38eSCan Guo 22403e5d38eSCan Guo return setting; 22503e5d38eSCan Guo } 22603e5d38eSCan Guo 22703e5d38eSCan Guo /** 22803e5d38eSCan Guo * ufshcd_apply_tx_eq_settings - Apply TX Equalization settings for target gear 22903e5d38eSCan Guo * @hba: per adapter instance 23003e5d38eSCan Guo * @params: TX EQ parameters data structure 23103e5d38eSCan Guo * @gear: target gear 23203e5d38eSCan Guo * 23303e5d38eSCan Guo * Returns 0 on success, negative error code otherwise 23403e5d38eSCan Guo */ 235*26605db7SCan Guo int ufshcd_apply_tx_eq_settings(struct ufs_hba *hba, 236*26605db7SCan Guo struct ufshcd_tx_eq_params *params, u32 gear) 23703e5d38eSCan Guo { 23803e5d38eSCan Guo struct ufs_pa_layer_attr *pwr_info = &hba->max_pwr_info.info; 23903e5d38eSCan Guo u32 setting; 24003e5d38eSCan Guo int ret; 24103e5d38eSCan Guo 24203e5d38eSCan Guo /* Compose settings for Host's TX Lanes */ 24303e5d38eSCan Guo setting = ufshcd_compose_tx_eq_setting(params->host, pwr_info->lane_tx); 24403e5d38eSCan Guo ret = ufshcd_dme_set(hba, UIC_ARG_MIB(pa_tx_eq_setting[gear - 1]), setting); 24503e5d38eSCan Guo if (ret) 24603e5d38eSCan Guo return ret; 24703e5d38eSCan Guo 24803e5d38eSCan Guo /* Compose settings for Device's TX Lanes */ 24903e5d38eSCan Guo setting = ufshcd_compose_tx_eq_setting(params->device, pwr_info->lane_rx); 25003e5d38eSCan Guo ret = ufshcd_dme_peer_set(hba, UIC_ARG_MIB(pa_tx_eq_setting[gear - 1]), setting); 25103e5d38eSCan Guo if (ret) 25203e5d38eSCan Guo return ret; 25303e5d38eSCan Guo 25403e5d38eSCan Guo /* Configure Pre-Coding */ 25503e5d38eSCan Guo if (gear >= UFS_HS_G6) { 25603e5d38eSCan Guo ret = ufshcd_configure_precoding(hba, params); 25703e5d38eSCan Guo if (ret) { 25803e5d38eSCan Guo dev_err(hba->dev, "Failed to configure pre-coding: %d\n", ret); 25903e5d38eSCan Guo return ret; 26003e5d38eSCan Guo } 26103e5d38eSCan Guo } 26203e5d38eSCan Guo 26303e5d38eSCan Guo return 0; 26403e5d38eSCan Guo } 265*26605db7SCan Guo EXPORT_SYMBOL_GPL(ufshcd_apply_tx_eq_settings); 26603e5d38eSCan Guo 26703e5d38eSCan Guo /** 26803e5d38eSCan Guo * ufshcd_evaluate_tx_eqtr_fom - Evaluate TX EQTR FOM results 26903e5d38eSCan Guo * @hba: per adapter instance 27003e5d38eSCan Guo * @pwr_mode: target power mode containing gear and rate information 27103e5d38eSCan Guo * @eqtr_data: TX EQTR data structure 27203e5d38eSCan Guo * @h_iter: host TX EQTR iterator data structure 27303e5d38eSCan Guo * @d_iter: device TX EQTR iterator data structure 27403e5d38eSCan Guo * 27503e5d38eSCan Guo * Evaluate TX EQTR FOM results, update host and device TX EQTR data accordingy 27603e5d38eSCan Guo * if FOM have been improved compared to previous iteration, and record TX EQTR 27703e5d38eSCan Guo * FOM results. 27803e5d38eSCan Guo */ 27903e5d38eSCan Guo static void ufshcd_evaluate_tx_eqtr_fom(struct ufs_hba *hba, 28003e5d38eSCan Guo struct ufs_pa_layer_attr *pwr_mode, 28103e5d38eSCan Guo struct ufshcd_tx_eqtr_data *eqtr_data, 28203e5d38eSCan Guo struct tx_eqtr_iter *h_iter, 28303e5d38eSCan Guo struct tx_eqtr_iter *d_iter) 28403e5d38eSCan Guo { 28503e5d38eSCan Guo u8 preshoot, deemphasis, fom_value; 28603e5d38eSCan Guo bool precode_en; 28703e5d38eSCan Guo int lane; 28803e5d38eSCan Guo 28903e5d38eSCan Guo for (lane = 0; h_iter->is_updated && lane < pwr_mode->lane_tx; lane++) { 29003e5d38eSCan Guo preshoot = h_iter->preshoot; 29103e5d38eSCan Guo deemphasis = h_iter->deemphasis; 29203e5d38eSCan Guo fom_value = h_iter->fom[lane] & RX_FOM_VALUE_MASK; 29303e5d38eSCan Guo precode_en = h_iter->fom[lane] & RX_FOM_PRECODING_EN_BIT; 29403e5d38eSCan Guo 29503e5d38eSCan Guo /* Record host TX EQTR FOM */ 29603e5d38eSCan Guo eqtr_data->host_fom[lane][preshoot][deemphasis] = h_iter->fom[lane]; 29703e5d38eSCan Guo 29803e5d38eSCan Guo /* Check if FOM has been improved for host's TX Lanes */ 29903e5d38eSCan Guo if (fom_value > eqtr_data->host[lane].fom_val) { 30003e5d38eSCan Guo eqtr_data->host[lane].preshoot = preshoot; 30103e5d38eSCan Guo eqtr_data->host[lane].deemphasis = deemphasis; 30203e5d38eSCan Guo eqtr_data->host[lane].fom_val = fom_value; 30303e5d38eSCan Guo eqtr_data->host[lane].precode_en = precode_en; 30403e5d38eSCan Guo } 30503e5d38eSCan Guo 30603e5d38eSCan Guo dev_dbg(hba->dev, "TX EQTR: Host TX Lane %d: PreShoot %u, DeEmphasis %u, FOM value %u, PreCodeEn %d\n", 30703e5d38eSCan Guo lane, preshoot, deemphasis, fom_value, precode_en); 30803e5d38eSCan Guo } 30903e5d38eSCan Guo 31003e5d38eSCan Guo for (lane = 0; d_iter->is_updated && lane < pwr_mode->lane_rx; lane++) { 31103e5d38eSCan Guo preshoot = d_iter->preshoot; 31203e5d38eSCan Guo deemphasis = d_iter->deemphasis; 31303e5d38eSCan Guo fom_value = d_iter->fom[lane] & RX_FOM_VALUE_MASK; 31403e5d38eSCan Guo precode_en = d_iter->fom[lane] & RX_FOM_PRECODING_EN_BIT; 31503e5d38eSCan Guo 31603e5d38eSCan Guo /* Record device TX EQTR FOM */ 31703e5d38eSCan Guo eqtr_data->device_fom[lane][preshoot][deemphasis] = d_iter->fom[lane]; 31803e5d38eSCan Guo 31903e5d38eSCan Guo /* Check if FOM has been improved for Device's TX Lanes */ 32003e5d38eSCan Guo if (fom_value > eqtr_data->device[lane].fom_val) { 32103e5d38eSCan Guo eqtr_data->device[lane].preshoot = preshoot; 32203e5d38eSCan Guo eqtr_data->device[lane].deemphasis = deemphasis; 32303e5d38eSCan Guo eqtr_data->device[lane].fom_val = fom_value; 32403e5d38eSCan Guo eqtr_data->device[lane].precode_en = precode_en; 32503e5d38eSCan Guo } 32603e5d38eSCan Guo 32703e5d38eSCan Guo dev_dbg(hba->dev, "TX EQTR: Device TX Lane %d: PreShoot %u, DeEmphasis %u, FOM value %u, PreCodeEn %d\n", 32803e5d38eSCan Guo lane, preshoot, deemphasis, fom_value, precode_en); 32903e5d38eSCan Guo } 33003e5d38eSCan Guo } 33103e5d38eSCan Guo 33203e5d38eSCan Guo /** 33303e5d38eSCan Guo * ufshcd_get_rx_fom - Get Figure of Merit (FOM) for both sides 33403e5d38eSCan Guo * @hba: per adapter instance 33503e5d38eSCan Guo * @pwr_mode: target power mode containing gear and rate information 33603e5d38eSCan Guo * @h_iter: host TX EQTR iterator data structure 33703e5d38eSCan Guo * @d_iter: device TX EQTR iterator data structure 33803e5d38eSCan Guo * 33903e5d38eSCan Guo * Returns 0 on success, negative error code otherwise 34003e5d38eSCan Guo */ 34103e5d38eSCan Guo static int ufshcd_get_rx_fom(struct ufs_hba *hba, 34203e5d38eSCan Guo struct ufs_pa_layer_attr *pwr_mode, 34303e5d38eSCan Guo struct tx_eqtr_iter *h_iter, 34403e5d38eSCan Guo struct tx_eqtr_iter *d_iter) 34503e5d38eSCan Guo { 34603e5d38eSCan Guo int lane, ret; 34703e5d38eSCan Guo u32 fom; 34803e5d38eSCan Guo 34903e5d38eSCan Guo /* Get FOM of host's TX lanes from device's RX_FOM. */ 35003e5d38eSCan Guo for (lane = 0; lane < pwr_mode->lane_tx; lane++) { 35103e5d38eSCan Guo ret = ufshcd_dme_peer_get(hba, UIC_ARG_MIB_SEL(RX_FOM, 35203e5d38eSCan Guo UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane)), 35303e5d38eSCan Guo &fom); 35403e5d38eSCan Guo if (ret) 35503e5d38eSCan Guo return ret; 35603e5d38eSCan Guo 35703e5d38eSCan Guo h_iter->fom[lane] = (u8)fom; 35803e5d38eSCan Guo } 35903e5d38eSCan Guo 36003e5d38eSCan Guo /* Get FOM of device's TX lanes from host's RX_FOM. */ 36103e5d38eSCan Guo for (lane = 0; lane < pwr_mode->lane_rx; lane++) { 36203e5d38eSCan Guo ret = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(RX_FOM, 36303e5d38eSCan Guo UIC_ARG_MPHY_RX_GEN_SEL_INDEX(lane)), 36403e5d38eSCan Guo &fom); 36503e5d38eSCan Guo if (ret) 36603e5d38eSCan Guo return ret; 36703e5d38eSCan Guo 36803e5d38eSCan Guo d_iter->fom[lane] = (u8)fom; 36903e5d38eSCan Guo } 37003e5d38eSCan Guo 37103e5d38eSCan Guo ret = ufshcd_vops_get_rx_fom(hba, pwr_mode, h_iter, d_iter); 37203e5d38eSCan Guo if (ret) 37303e5d38eSCan Guo dev_err(hba->dev, "Failed to get FOM via vops: %d\n", ret); 37403e5d38eSCan Guo 37503e5d38eSCan Guo return ret; 37603e5d38eSCan Guo } 37703e5d38eSCan Guo 37810c40143SCan Guo bool ufshcd_is_txeq_presets_used(struct ufs_hba *hba) 37910c40143SCan Guo { 38010c40143SCan Guo return use_txeq_presets; 38110c40143SCan Guo } 38210c40143SCan Guo 38310c40143SCan Guo bool ufshcd_is_txeq_preset_selected(u8 preshoot, u8 deemphasis) 38403e5d38eSCan Guo { 38503e5d38eSCan Guo int i; 38603e5d38eSCan Guo 38703e5d38eSCan Guo for (i = 0; i < UFS_TX_EQ_PRESET_MAX; i++) { 38803e5d38eSCan Guo if (!txeq_presets_selected[i]) 38903e5d38eSCan Guo continue; 39003e5d38eSCan Guo 39103e5d38eSCan Guo if (preshoot == ufs_tx_eq_preset[i].preshoot && 39203e5d38eSCan Guo deemphasis == ufs_tx_eq_preset[i].deemphasis) 39303e5d38eSCan Guo return true; 39403e5d38eSCan Guo } 39503e5d38eSCan Guo 39603e5d38eSCan Guo return false; 39703e5d38eSCan Guo } 39803e5d38eSCan Guo 39903e5d38eSCan Guo /** 40003e5d38eSCan Guo * tx_eqtr_iter_try_update - Try to update a TX EQTR iterator 40103e5d38eSCan Guo * @iter: TX EQTR iterator data structure 40203e5d38eSCan Guo * @preshoot: PreShoot value 40303e5d38eSCan Guo * @deemphasis: DeEmphasis value 40403e5d38eSCan Guo * 40503e5d38eSCan Guo * This function validates whether the provided PreShoot and DeEmphasis 40603e5d38eSCan Guo * combination can be used or not. If yes, it updates the TX EQTR iterator with 40703e5d38eSCan Guo * the provided PreShoot and DeEmphasis, it also sets the is_updated flag 40803e5d38eSCan Guo * to indicate the iterator has been updated. 40903e5d38eSCan Guo */ 41003e5d38eSCan Guo static void tx_eqtr_iter_try_update(struct tx_eqtr_iter *iter, 41103e5d38eSCan Guo u8 preshoot, u8 deemphasis) 41203e5d38eSCan Guo { 41303e5d38eSCan Guo if (!test_bit(preshoot, &iter->preshoot_bitmap) || 41403e5d38eSCan Guo !test_bit(deemphasis, &iter->deemphasis_bitmap) || 41503e5d38eSCan Guo (use_txeq_presets && !ufshcd_is_txeq_preset_selected(preshoot, deemphasis))) { 41603e5d38eSCan Guo iter->is_updated = false; 41703e5d38eSCan Guo return; 41803e5d38eSCan Guo } 41903e5d38eSCan Guo 42003e5d38eSCan Guo iter->preshoot = preshoot; 42103e5d38eSCan Guo iter->deemphasis = deemphasis; 42203e5d38eSCan Guo iter->is_updated = true; 42303e5d38eSCan Guo } 42403e5d38eSCan Guo 42503e5d38eSCan Guo /** 42603e5d38eSCan Guo * tx_eqtr_iter_update() - Update host and deviceTX EQTR iterators 42703e5d38eSCan Guo * @preshoot: PreShoot value 42803e5d38eSCan Guo * @deemphasis: DeEmphasis value 42903e5d38eSCan Guo * @h_iter: Host TX EQTR iterator data structure 43003e5d38eSCan Guo * @d_iter: Device TX EQTR iterator data structure 43103e5d38eSCan Guo * 43203e5d38eSCan Guo * Updates host and device TX Equalization training iterators with the 43303e5d38eSCan Guo * provided PreShoot and DeEmphasis. 43403e5d38eSCan Guo * 43503e5d38eSCan Guo * Return: true if host and/or device TX Equalization training iterator has 43603e5d38eSCan Guo * been updated to the provided PreShoot and DeEmphasis, false otherwise. 43703e5d38eSCan Guo */ 43803e5d38eSCan Guo static bool tx_eqtr_iter_update(u8 preshoot, u8 deemphasis, 43903e5d38eSCan Guo struct tx_eqtr_iter *h_iter, 44003e5d38eSCan Guo struct tx_eqtr_iter *d_iter) 44103e5d38eSCan Guo { 44203e5d38eSCan Guo tx_eqtr_iter_try_update(h_iter, preshoot, deemphasis); 44303e5d38eSCan Guo tx_eqtr_iter_try_update(d_iter, preshoot, deemphasis); 44403e5d38eSCan Guo 44503e5d38eSCan Guo return h_iter->is_updated || d_iter->is_updated; 44603e5d38eSCan Guo } 44703e5d38eSCan Guo 44803e5d38eSCan Guo /** 44903e5d38eSCan Guo * ufshcd_tx_eqtr_iter_init - Initialize host and device TX EQTR iterators 45003e5d38eSCan Guo * @hba: per adapter instance 45103e5d38eSCan Guo * @h_iter: host TX EQTR iterator data structure 45203e5d38eSCan Guo * @d_iter: device TX EQTR iterator data structure 45303e5d38eSCan Guo * 45403e5d38eSCan Guo * This function initializes the TX EQTR iterator structures for both host and 45503e5d38eSCan Guo * device by reading their TX equalization capabilities. The capabilities are 45603e5d38eSCan Guo * cached in the hba structure to avoid redundant DME operations in subsequent 45703e5d38eSCan Guo * calls. In the TX EQTR procedure, the iterator structures are updated by 45803e5d38eSCan Guo * tx_eqtr_iter_update() to systematically iterate through supported TX 45903e5d38eSCan Guo * Equalization setting combinations. 46003e5d38eSCan Guo * 46103e5d38eSCan Guo * Returns 0 on success, negative error code otherwise 46203e5d38eSCan Guo */ 46303e5d38eSCan Guo static int ufshcd_tx_eqtr_iter_init(struct ufs_hba *hba, 46403e5d38eSCan Guo struct tx_eqtr_iter *h_iter, 46503e5d38eSCan Guo struct tx_eqtr_iter *d_iter) 46603e5d38eSCan Guo { 46703e5d38eSCan Guo u32 cap; 46803e5d38eSCan Guo int ret; 46903e5d38eSCan Guo 47003e5d38eSCan Guo if (!hba->host_preshoot_cap) { 47103e5d38eSCan Guo ret = ufshcd_dme_get(hba, UIC_ARG_MIB(TX_HS_PRESHOOT_SETTING_CAP), &cap); 47203e5d38eSCan Guo if (ret) 47303e5d38eSCan Guo return ret; 47403e5d38eSCan Guo 47503e5d38eSCan Guo hba->host_preshoot_cap = cap & TX_EQTR_CAP_MASK; 47603e5d38eSCan Guo } 47703e5d38eSCan Guo 47803e5d38eSCan Guo if (!hba->host_deemphasis_cap) { 47903e5d38eSCan Guo ret = ufshcd_dme_get(hba, UIC_ARG_MIB(TX_HS_DEEMPHASIS_SETTING_CAP), &cap); 48003e5d38eSCan Guo if (ret) 48103e5d38eSCan Guo return ret; 48203e5d38eSCan Guo 48303e5d38eSCan Guo hba->host_deemphasis_cap = cap & TX_EQTR_CAP_MASK; 48403e5d38eSCan Guo } 48503e5d38eSCan Guo 48603e5d38eSCan Guo if (!hba->device_preshoot_cap) { 48703e5d38eSCan Guo ret = ufshcd_dme_peer_get(hba, UIC_ARG_MIB(TX_HS_PRESHOOT_SETTING_CAP), &cap); 48803e5d38eSCan Guo if (ret) 48903e5d38eSCan Guo return ret; 49003e5d38eSCan Guo 49103e5d38eSCan Guo hba->device_preshoot_cap = cap & TX_EQTR_CAP_MASK; 49203e5d38eSCan Guo } 49303e5d38eSCan Guo 49403e5d38eSCan Guo if (!hba->device_deemphasis_cap) { 49503e5d38eSCan Guo ret = ufshcd_dme_peer_get(hba, UIC_ARG_MIB(TX_HS_DEEMPHASIS_SETTING_CAP), &cap); 49603e5d38eSCan Guo if (ret) 49703e5d38eSCan Guo return ret; 49803e5d38eSCan Guo 49903e5d38eSCan Guo hba->device_deemphasis_cap = cap & TX_EQTR_CAP_MASK; 50003e5d38eSCan Guo } 50103e5d38eSCan Guo 50203e5d38eSCan Guo /* 50303e5d38eSCan Guo * Support PreShoot & DeEmphasis of value 0 is mandatory, hence they are 50403e5d38eSCan Guo * not reflected in PreShoot/DeEmphasis capabilities. Left shift the 50503e5d38eSCan Guo * capability bitmap by 1 and set bit[0] to reflect value 0 is 50603e5d38eSCan Guo * supported, such that test_bit() can be used later for convenience. 50703e5d38eSCan Guo */ 50803e5d38eSCan Guo h_iter->preshoot_bitmap = (hba->host_preshoot_cap << 0x1) | 0x1; 50903e5d38eSCan Guo h_iter->deemphasis_bitmap = (hba->host_deemphasis_cap << 0x1) | 0x1; 51003e5d38eSCan Guo d_iter->preshoot_bitmap = (hba->device_preshoot_cap << 0x1) | 0x1; 51103e5d38eSCan Guo d_iter->deemphasis_bitmap = (hba->device_deemphasis_cap << 0x1) | 0x1; 51203e5d38eSCan Guo 51303e5d38eSCan Guo return 0; 51403e5d38eSCan Guo } 51503e5d38eSCan Guo 51603e5d38eSCan Guo /** 51703e5d38eSCan Guo * adapt_cap_to_t_adapt - Calculate TAdapt from adapt capability 51803e5d38eSCan Guo * @adapt_cap: Adapt capability 51903e5d38eSCan Guo * 52003e5d38eSCan Guo * For NRZ: 52103e5d38eSCan Guo * IF (ADAPT_range = FINE) 52203e5d38eSCan Guo * TADAPT = 650 x (ADAPT_length + 1) 52303e5d38eSCan Guo * ELSE (IF ADAPT_range = COARSE) 52403e5d38eSCan Guo * TADAPT = 650 x 2^ADAPT_length 52503e5d38eSCan Guo * 52603e5d38eSCan Guo * Returns calculated TAdapt value in term of Unit Intervals (UI) 52703e5d38eSCan Guo */ 52803e5d38eSCan Guo static inline u64 adapt_cap_to_t_adapt(u32 adapt_cap) 52903e5d38eSCan Guo { 53003e5d38eSCan Guo u64 tadapt; 53103e5d38eSCan Guo u8 adapt_length = adapt_cap & ADAPT_LENGTH_MASK; 53203e5d38eSCan Guo 53303e5d38eSCan Guo if (!IS_ADAPT_RANGE_COARSE(adapt_cap)) 53403e5d38eSCan Guo tadapt = TADAPT_FACTOR * (adapt_length + 1); 53503e5d38eSCan Guo else 53603e5d38eSCan Guo tadapt = TADAPT_FACTOR * (1 << adapt_length); 53703e5d38eSCan Guo 53803e5d38eSCan Guo return tadapt; 53903e5d38eSCan Guo } 54003e5d38eSCan Guo 54103e5d38eSCan Guo /** 54203e5d38eSCan Guo * adapt_cap_to_t_adapt_l0l3 - Calculate TAdapt_L0_L3 from adapt capability 54303e5d38eSCan Guo * @adapt_cap: Adapt capability 54403e5d38eSCan Guo * 54503e5d38eSCan Guo * For PAM-4: 54603e5d38eSCan Guo * IF (ADAPT_range = FINE) 54703e5d38eSCan Guo * TADAPT_L0_L3 = 2^9 x ADAPT_length 54803e5d38eSCan Guo * ELSE IF (ADAPT_range = COARSE) 54903e5d38eSCan Guo * TADAPT_L0_L3 = 2^9 x (2^ADAPT_length) 55003e5d38eSCan Guo * 55103e5d38eSCan Guo * Returns calculated TAdapt value in term of Unit Intervals (UI) 55203e5d38eSCan Guo */ 55303e5d38eSCan Guo static inline u64 adapt_cap_to_t_adapt_l0l3(u32 adapt_cap) 55403e5d38eSCan Guo { 55503e5d38eSCan Guo u64 tadapt; 55603e5d38eSCan Guo u8 adapt_length = adapt_cap & ADAPT_LENGTH_MASK; 55703e5d38eSCan Guo 55803e5d38eSCan Guo if (!IS_ADAPT_RANGE_COARSE(adapt_cap)) 55903e5d38eSCan Guo tadapt = TADAPT_L0L3_FACTOR * adapt_length; 56003e5d38eSCan Guo else 56103e5d38eSCan Guo tadapt = TADAPT_L0L3_FACTOR * (1 << adapt_length); 56203e5d38eSCan Guo 56303e5d38eSCan Guo return tadapt; 56403e5d38eSCan Guo } 56503e5d38eSCan Guo 56603e5d38eSCan Guo /** 56703e5d38eSCan Guo * adapt_cap_to_t_adapt_l0l1l2l3 - Calculate TAdapt_L0_L1_L2_L3 from adapt capability 56803e5d38eSCan Guo * @adapt_cap: Adapt capability 56903e5d38eSCan Guo * 57003e5d38eSCan Guo * For PAM-4: 57103e5d38eSCan Guo * IF (ADAPT_range_L0_L1_L2_L3 = FINE) 57203e5d38eSCan Guo * TADAPT_L0_L1_L2_L3 = 2^15 x (ADAPT_length_L0_L1_L2_L3 + 1) 57303e5d38eSCan Guo * ELSE IF (ADAPT_range_L0_L1_L2_L3 = COARSE) 57403e5d38eSCan Guo * TADAPT_L0_L1_L2_L3 = 2^15 x 2^ADAPT_length_L0_L1_L2_L3 57503e5d38eSCan Guo * 57603e5d38eSCan Guo * Returns calculated TAdapt value in term of Unit Intervals (UI) 57703e5d38eSCan Guo */ 57803e5d38eSCan Guo static inline u64 adapt_cap_to_t_adapt_l0l1l2l3(u32 adapt_cap) 57903e5d38eSCan Guo { 58003e5d38eSCan Guo u64 tadapt; 58103e5d38eSCan Guo u8 adapt_length = adapt_cap & ADAPT_LENGTH_MASK; 58203e5d38eSCan Guo 58303e5d38eSCan Guo if (!IS_ADAPT_RANGE_COARSE(adapt_cap)) 58403e5d38eSCan Guo tadapt = TADAPT_L0L1L2L3_FACTOR * (adapt_length + 1); 58503e5d38eSCan Guo else 58603e5d38eSCan Guo tadapt = TADAPT_L0L1L2L3_FACTOR * (1 << adapt_length); 58703e5d38eSCan Guo 58803e5d38eSCan Guo return tadapt; 58903e5d38eSCan Guo } 59003e5d38eSCan Guo 59103e5d38eSCan Guo /** 59203e5d38eSCan Guo * ufshcd_setup_tx_eqtr_adapt_length - Setup TX adapt length for EQTR 59303e5d38eSCan Guo * @hba: per adapter instance 59403e5d38eSCan Guo * @params: TX EQ parameters data structure 59503e5d38eSCan Guo * @gear: target gear for EQTR 59603e5d38eSCan Guo * 59703e5d38eSCan Guo * This function determines and configures the proper TX adapt length (TAdapt) 59803e5d38eSCan Guo * for the TX EQTR procedure based on the target gear and RX adapt capabilities 59903e5d38eSCan Guo * of both host and device. 60003e5d38eSCan Guo * 60103e5d38eSCan Guo * Guidelines from MIPI UniPro v3.0 spec - select the minimum Adapt Length for 60203e5d38eSCan Guo * the Equalization Training procedure based on the following conditions: 60303e5d38eSCan Guo * 60403e5d38eSCan Guo * If the target High-Speed Gear n is HS-G4 or HS-G5: 60503e5d38eSCan Guo * PA_TxAdaptLength_EQTR[7:0] >= Max (10us, RX_HS_Gn_ADAPT_INITIAL_Capability, 60603e5d38eSCan Guo * PA_PeerRxHsGnAdaptInitial) 60703e5d38eSCan Guo * PA_TxAdaptLength_EQTR[7:0] shall be shorter than PACP_REQUEST_TIMER (10ms) 60803e5d38eSCan Guo * PA_TxAdaptLength_EQTR[15:8] is not relevant for HS-G4 and HS-G5. This field 60903e5d38eSCan Guo * is set to 255 (reserved value). 61003e5d38eSCan Guo * 61103e5d38eSCan Guo * If the target High-Speed Gear n is HS-G6: 61203e5d38eSCan Guo * PA_TxAdapthLength_EQTR >= 10us 61303e5d38eSCan Guo * PA_TxAdapthLength_EQTR[7:0] >= Max (RX_HS_G6_ADAPT_INITIAL_Capability, 61403e5d38eSCan Guo * PA_PeerRxHsG6AdaptInitialL0L3) 61503e5d38eSCan Guo * PA_TxAdapthLength_EQTR[15:8] >= Max (RX_HS_G6_ADAPT_INITIAL_L0_L1_L2_L3_Capability, 61603e5d38eSCan Guo * PA_PeerRxHsG6AdaptInitialL0L1L2L3) 61703e5d38eSCan Guo * PA_TxAdaptLength_EQTR shall be shorter than PACP_REQUEST_TIMER value of 10ms. 61803e5d38eSCan Guo * 61903e5d38eSCan Guo * Since adapt capabilities encode both range (fine/coarse) and length values, 62003e5d38eSCan Guo * direct comparison is not possible. This function converts adapt capabilities 62103e5d38eSCan Guo * to actual time durations in Unit Intervals (UI) using the Adapt time 62203e5d38eSCan Guo * calculation formular in M-PHY v6.0 spec (Table 8), then selects the maximum 62303e5d38eSCan Guo * to ensure both host and device use adequate TX adapt length. 62403e5d38eSCan Guo * 62503e5d38eSCan Guo * Returns 0 on success, negative error code otherwise 62603e5d38eSCan Guo */ 62703e5d38eSCan Guo static int ufshcd_setup_tx_eqtr_adapt_length(struct ufs_hba *hba, 62803e5d38eSCan Guo struct ufshcd_tx_eq_params *params, 62903e5d38eSCan Guo u32 gear) 63003e5d38eSCan Guo { 631adbabdcfSCan Guo struct ufshcd_tx_eqtr_record *rec = params->eqtr_record; 63203e5d38eSCan Guo u32 adapt_eqtr; 63303e5d38eSCan Guo int ret; 63403e5d38eSCan Guo 635adbabdcfSCan Guo if (rec && rec->saved_adapt_eqtr) { 636adbabdcfSCan Guo adapt_eqtr = rec->saved_adapt_eqtr; 637adbabdcfSCan Guo goto set_adapt_eqtr; 638adbabdcfSCan Guo } 639adbabdcfSCan Guo 64003e5d38eSCan Guo if (gear == UFS_HS_G4 || gear == UFS_HS_G5) { 64103e5d38eSCan Guo u64 t_adapt, t_adapt_local, t_adapt_peer; 64203e5d38eSCan Guo u32 adapt_cap_local, adapt_cap_peer, adapt_length; 64303e5d38eSCan Guo 64403e5d38eSCan Guo ret = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(rx_adapt_initial_cap[gear - 1], 64503e5d38eSCan Guo UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), 64603e5d38eSCan Guo &adapt_cap_local); 64703e5d38eSCan Guo if (ret) 64803e5d38eSCan Guo return ret; 64903e5d38eSCan Guo 65003e5d38eSCan Guo if (adapt_cap_local > ADAPT_LENGTH_MAX) { 65103e5d38eSCan Guo dev_err(hba->dev, "local RX_HS_G%u_ADAPT_INITIAL_CAP (0x%x) exceeds MAX\n", 65203e5d38eSCan Guo gear, adapt_cap_local); 65303e5d38eSCan Guo return -EINVAL; 65403e5d38eSCan Guo } 65503e5d38eSCan Guo 65603e5d38eSCan Guo ret = ufshcd_dme_get(hba, UIC_ARG_MIB(pa_peer_rx_adapt_initial[gear - 1]), 65703e5d38eSCan Guo &adapt_cap_peer); 65803e5d38eSCan Guo if (ret) 65903e5d38eSCan Guo return ret; 66003e5d38eSCan Guo 66103e5d38eSCan Guo if (adapt_cap_peer > ADAPT_LENGTH_MAX) { 66203e5d38eSCan Guo dev_err(hba->dev, "local RX_HS_G%u_ADAPT_INITIAL_CAP (0x%x) exceeds MAX\n", 66303e5d38eSCan Guo gear, adapt_cap_peer); 66403e5d38eSCan Guo return -EINVAL; 66503e5d38eSCan Guo } 66603e5d38eSCan Guo 66703e5d38eSCan Guo t_adapt_local = adapt_cap_to_t_adapt(adapt_cap_local); 66803e5d38eSCan Guo t_adapt_peer = adapt_cap_to_t_adapt(adapt_cap_peer); 66903e5d38eSCan Guo t_adapt = max(t_adapt_local, t_adapt_peer); 67003e5d38eSCan Guo 67103e5d38eSCan Guo dev_dbg(hba->dev, "local RX_HS_G%u_ADAPT_INITIAL_CAP = 0x%x\n", 67203e5d38eSCan Guo gear, adapt_cap_local); 67303e5d38eSCan Guo dev_dbg(hba->dev, "peer RX_HS_G%u_ADAPT_INITIAL_CAP = 0x%x\n", 67403e5d38eSCan Guo gear, adapt_cap_peer); 67503e5d38eSCan Guo dev_dbg(hba->dev, "t_adapt_local = %llu UI, t_adapt_peer = %llu UI\n", 67603e5d38eSCan Guo t_adapt_local, t_adapt_peer); 67703e5d38eSCan Guo dev_dbg(hba->dev, "TAdapt %llu UI selected for TX EQTR\n", 67803e5d38eSCan Guo t_adapt); 67903e5d38eSCan Guo 68003e5d38eSCan Guo adapt_length = (t_adapt_local >= t_adapt_peer) ? 68103e5d38eSCan Guo adapt_cap_local : adapt_cap_peer; 68203e5d38eSCan Guo 68303e5d38eSCan Guo if (gear == UFS_HS_G4 && t_adapt < TX_EQTR_HS_G4_MIN_T_ADAPT) { 68403e5d38eSCan Guo dev_dbg(hba->dev, "TAdapt %llu UI is too short for TX EQTR for HS-G%u, use default Adapt 0x%x\n", 68503e5d38eSCan Guo t_adapt, gear, TX_EQTR_HS_G4_ADAPT_DEFAULT); 68603e5d38eSCan Guo adapt_length = TX_EQTR_HS_G4_ADAPT_DEFAULT; 68703e5d38eSCan Guo } else if (gear == UFS_HS_G5 && t_adapt < TX_EQTR_HS_G5_MIN_T_ADAPT) { 68803e5d38eSCan Guo dev_dbg(hba->dev, "TAdapt %llu UI is too short for TX EQTR for HS-G%u, use default Adapt 0x%x\n", 68903e5d38eSCan Guo t_adapt, gear, TX_EQTR_HS_G5_ADAPT_DEFAULT); 69003e5d38eSCan Guo adapt_length = TX_EQTR_HS_G5_ADAPT_DEFAULT; 69103e5d38eSCan Guo } 69203e5d38eSCan Guo 69303e5d38eSCan Guo adapt_eqtr = adapt_length | 69403e5d38eSCan Guo (TX_EQTR_ADAPT_RESERVED << TX_EQTR_ADAPT_LENGTH_L0L1L2L3_SHIFT); 69503e5d38eSCan Guo } else if (gear == UFS_HS_G6) { 69603e5d38eSCan Guo u64 t_adapt, t_adapt_l0l3, t_adapt_l0l3_local, t_adapt_l0l3_peer; 69703e5d38eSCan Guo u64 t_adapt_l0l1l2l3, t_adapt_l0l1l2l3_local, t_adapt_l0l1l2l3_peer; 69803e5d38eSCan Guo u32 adapt_l0l3_cap_local, adapt_l0l3_cap_peer, adapt_length_l0l3; 69903e5d38eSCan Guo u32 adapt_l0l1l2l3_cap_local, adapt_l0l1l2l3_cap_peer, adapt_length_l0l1l2l3; 70003e5d38eSCan Guo 70103e5d38eSCan Guo ret = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(rx_adapt_initial_cap[gear - 1], 70203e5d38eSCan Guo UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), 70303e5d38eSCan Guo &adapt_l0l3_cap_local); 70403e5d38eSCan Guo if (ret) 70503e5d38eSCan Guo return ret; 70603e5d38eSCan Guo 70703e5d38eSCan Guo if (adapt_l0l3_cap_local > ADAPT_L0L3_LENGTH_MAX) { 70803e5d38eSCan Guo dev_err(hba->dev, "local RX_HS_G%u_ADAPT_INITIAL_CAP (0x%x) exceeds MAX\n", 70903e5d38eSCan Guo gear, adapt_l0l3_cap_local); 71003e5d38eSCan Guo return -EINVAL; 71103e5d38eSCan Guo } 71203e5d38eSCan Guo 71303e5d38eSCan Guo ret = ufshcd_dme_get(hba, UIC_ARG_MIB(pa_peer_rx_adapt_initial[gear - 1]), 71403e5d38eSCan Guo &adapt_l0l3_cap_peer); 71503e5d38eSCan Guo if (ret) 71603e5d38eSCan Guo return ret; 71703e5d38eSCan Guo 71803e5d38eSCan Guo if (adapt_l0l3_cap_peer > ADAPT_L0L3_LENGTH_MAX) { 71903e5d38eSCan Guo dev_err(hba->dev, "peer RX_HS_G%u_ADAPT_INITIAL_CAP (0x%x) exceeds MAX\n", 72003e5d38eSCan Guo gear, adapt_l0l3_cap_peer); 72103e5d38eSCan Guo return -EINVAL; 72203e5d38eSCan Guo } 72303e5d38eSCan Guo 72403e5d38eSCan Guo t_adapt_l0l3_local = adapt_cap_to_t_adapt_l0l3(adapt_l0l3_cap_local); 72503e5d38eSCan Guo t_adapt_l0l3_peer = adapt_cap_to_t_adapt_l0l3(adapt_l0l3_cap_peer); 72603e5d38eSCan Guo 72703e5d38eSCan Guo dev_dbg(hba->dev, "local RX_HS_G%u_ADAPT_INITIAL_CAP = 0x%x\n", 72803e5d38eSCan Guo gear, adapt_l0l3_cap_local); 72903e5d38eSCan Guo dev_dbg(hba->dev, "peer RX_HS_G%u_ADAPT_INITIAL_CAP = 0x%x\n", 73003e5d38eSCan Guo gear, adapt_l0l3_cap_peer); 73103e5d38eSCan Guo dev_dbg(hba->dev, "t_adapt_l0l3_local = %llu UI, t_adapt_l0l3_peer = %llu UI\n", 73203e5d38eSCan Guo t_adapt_l0l3_local, t_adapt_l0l3_peer); 73303e5d38eSCan Guo 73403e5d38eSCan Guo ret = ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(RX_HS_G6_ADAPT_INITIAL_L0L1L2L3_CAP, 73503e5d38eSCan Guo UIC_ARG_MPHY_RX_GEN_SEL_INDEX(0)), 73603e5d38eSCan Guo &adapt_l0l1l2l3_cap_local); 73703e5d38eSCan Guo if (ret) 73803e5d38eSCan Guo return ret; 73903e5d38eSCan Guo 74003e5d38eSCan Guo if (adapt_l0l1l2l3_cap_local > ADAPT_L0L1L2L3_LENGTH_MAX) { 74103e5d38eSCan Guo dev_err(hba->dev, "local RX_HS_G%u_ADAPT_INITIAL_L0L1L2L3_CAP (0x%x) exceeds MAX\n", 74203e5d38eSCan Guo gear, adapt_l0l1l2l3_cap_local); 74303e5d38eSCan Guo return -EINVAL; 74403e5d38eSCan Guo } 74503e5d38eSCan Guo 74603e5d38eSCan Guo ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_PEERRXHSG6ADAPTINITIALL0L1L2L3), 74703e5d38eSCan Guo &adapt_l0l1l2l3_cap_peer); 74803e5d38eSCan Guo if (ret) 74903e5d38eSCan Guo return ret; 75003e5d38eSCan Guo 75103e5d38eSCan Guo if (adapt_l0l1l2l3_cap_peer > ADAPT_L0L1L2L3_LENGTH_MAX) { 75203e5d38eSCan Guo dev_err(hba->dev, "peer RX_HS_G%u_ADAPT_INITIAL_L0L1L2L3_CAP (0x%x) exceeds MAX\n", 75303e5d38eSCan Guo gear, adapt_l0l1l2l3_cap_peer); 75403e5d38eSCan Guo return -EINVAL; 75503e5d38eSCan Guo } 75603e5d38eSCan Guo 75703e5d38eSCan Guo t_adapt_l0l1l2l3_local = adapt_cap_to_t_adapt_l0l1l2l3(adapt_l0l1l2l3_cap_local); 75803e5d38eSCan Guo t_adapt_l0l1l2l3_peer = adapt_cap_to_t_adapt_l0l1l2l3(adapt_l0l1l2l3_cap_peer); 75903e5d38eSCan Guo 76003e5d38eSCan Guo dev_dbg(hba->dev, "local RX_HS_G%u_ADAPT_INITIAL_L0L1L2L3_CAP = 0x%x\n", 76103e5d38eSCan Guo gear, adapt_l0l1l2l3_cap_local); 76203e5d38eSCan Guo dev_dbg(hba->dev, "peer RX_HS_G%u_ADAPT_INITIAL_L0L1L2L3_CAP = 0x%x\n", 76303e5d38eSCan Guo gear, adapt_l0l1l2l3_cap_peer); 76403e5d38eSCan Guo dev_dbg(hba->dev, "t_adapt_l0l1l2l3_local = %llu UI, t_adapt_l0l1l2l3_peer = %llu UI\n", 76503e5d38eSCan Guo t_adapt_l0l1l2l3_local, t_adapt_l0l1l2l3_peer); 76603e5d38eSCan Guo 76703e5d38eSCan Guo t_adapt_l0l1l2l3 = max(t_adapt_l0l1l2l3_local, t_adapt_l0l1l2l3_peer); 76803e5d38eSCan Guo t_adapt_l0l3 = max(t_adapt_l0l3_local, t_adapt_l0l3_peer); 76903e5d38eSCan Guo t_adapt = t_adapt_l0l3 + t_adapt_l0l1l2l3; 77003e5d38eSCan Guo 77103e5d38eSCan Guo dev_dbg(hba->dev, "TAdapt %llu PAM-4 UI selected for TX EQTR\n", 77203e5d38eSCan Guo t_adapt); 77303e5d38eSCan Guo 77403e5d38eSCan Guo adapt_length_l0l3 = (t_adapt_l0l3_local >= t_adapt_l0l3_peer) ? 77503e5d38eSCan Guo adapt_l0l3_cap_local : adapt_l0l3_cap_peer; 77603e5d38eSCan Guo adapt_length_l0l1l2l3 = (t_adapt_l0l1l2l3_local >= t_adapt_l0l1l2l3_peer) ? 77703e5d38eSCan Guo adapt_l0l1l2l3_cap_local : adapt_l0l1l2l3_cap_peer; 77803e5d38eSCan Guo 77903e5d38eSCan Guo if (t_adapt < TX_EQTR_HS_G6_MIN_T_ADAPT) { 78003e5d38eSCan Guo dev_dbg(hba->dev, "TAdapt %llu UI is too short for TX EQTR for HS-G%u, use default Adapt 0x%x\n", 78103e5d38eSCan Guo t_adapt, gear, TX_EQTR_HS_G6_ADAPT_DEFAULT); 78203e5d38eSCan Guo adapt_length_l0l3 = TX_EQTR_HS_G6_ADAPT_DEFAULT; 78303e5d38eSCan Guo } 78403e5d38eSCan Guo 78503e5d38eSCan Guo adapt_eqtr = adapt_length_l0l3 | 78603e5d38eSCan Guo (adapt_length_l0l1l2l3 << TX_EQTR_ADAPT_LENGTH_L0L1L2L3_SHIFT); 78703e5d38eSCan Guo } else { 78803e5d38eSCan Guo return -EINVAL; 78903e5d38eSCan Guo } 79003e5d38eSCan Guo 791adbabdcfSCan Guo if (rec) 792adbabdcfSCan Guo rec->saved_adapt_eqtr = (u16)adapt_eqtr; 793adbabdcfSCan Guo 794adbabdcfSCan Guo set_adapt_eqtr: 79503e5d38eSCan Guo ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXADAPTLENGTH_EQTR), adapt_eqtr); 79603e5d38eSCan Guo if (ret) 79703e5d38eSCan Guo dev_err(hba->dev, "Failed to set adapt length for TX EQTR: %d\n", ret); 79803e5d38eSCan Guo else 79903e5d38eSCan Guo dev_dbg(hba->dev, "PA_TXADAPTLENGTH_EQTR configured to 0x%08x\n", adapt_eqtr); 80003e5d38eSCan Guo 80103e5d38eSCan Guo return ret; 80203e5d38eSCan Guo } 80303e5d38eSCan Guo 80403e5d38eSCan Guo /** 80503e5d38eSCan Guo * ufshcd_compose_tx_eqtr_setting - Compose TX EQTR setting 80603e5d38eSCan Guo * @iter: TX EQTR iterator data structure 80703e5d38eSCan Guo * @num_lanes: number of active lanes 80803e5d38eSCan Guo * 80903e5d38eSCan Guo * Returns composed TX EQTR setting, same setting is used for all active lanes 81003e5d38eSCan Guo */ 81103e5d38eSCan Guo static inline u32 ufshcd_compose_tx_eqtr_setting(struct tx_eqtr_iter *iter, 81203e5d38eSCan Guo int num_lanes) 81303e5d38eSCan Guo { 81403e5d38eSCan Guo u32 setting = 0; 81503e5d38eSCan Guo int lane; 81603e5d38eSCan Guo 81703e5d38eSCan Guo for (lane = 0; lane < num_lanes; lane++) { 81803e5d38eSCan Guo setting |= TX_HS_PRESHOOT_BITS(lane, iter->preshoot); 81903e5d38eSCan Guo setting |= TX_HS_DEEMPHASIS_BITS(lane, iter->deemphasis); 82003e5d38eSCan Guo } 82103e5d38eSCan Guo 82203e5d38eSCan Guo return setting; 82303e5d38eSCan Guo } 82403e5d38eSCan Guo 82503e5d38eSCan Guo /** 82603e5d38eSCan Guo * ufshcd_apply_tx_eqtr_settings - Apply TX EQTR setting 82703e5d38eSCan Guo * @hba: per adapter instance 82803e5d38eSCan Guo * @pwr_mode: target power mode containing gear and rate information 82903e5d38eSCan Guo * @h_iter: host TX EQTR iterator data structure 83003e5d38eSCan Guo * @d_iter: device TX EQTR iterator data structure 83103e5d38eSCan Guo * 83203e5d38eSCan Guo * Returns 0 on success, negative error code otherwise 83303e5d38eSCan Guo */ 83403e5d38eSCan Guo static int ufshcd_apply_tx_eqtr_settings(struct ufs_hba *hba, 83503e5d38eSCan Guo struct ufs_pa_layer_attr *pwr_mode, 83603e5d38eSCan Guo struct tx_eqtr_iter *h_iter, 83703e5d38eSCan Guo struct tx_eqtr_iter *d_iter) 83803e5d38eSCan Guo { 83903e5d38eSCan Guo u32 setting; 84003e5d38eSCan Guo int ret; 84103e5d38eSCan Guo 84203e5d38eSCan Guo setting = ufshcd_compose_tx_eqtr_setting(h_iter, pwr_mode->lane_tx); 84303e5d38eSCan Guo ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXEQTRSETTING), setting); 84403e5d38eSCan Guo if (ret) 84503e5d38eSCan Guo return ret; 84603e5d38eSCan Guo 84703e5d38eSCan Guo setting = ufshcd_compose_tx_eqtr_setting(d_iter, pwr_mode->lane_rx); 84803e5d38eSCan Guo ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PEERTXEQTRSETTING), setting); 84903e5d38eSCan Guo if (ret) 85003e5d38eSCan Guo return ret; 85103e5d38eSCan Guo 85203e5d38eSCan Guo ret = ufshcd_vops_apply_tx_eqtr_settings(hba, pwr_mode, h_iter, d_iter); 85303e5d38eSCan Guo 85403e5d38eSCan Guo return ret; 85503e5d38eSCan Guo } 85603e5d38eSCan Guo 85703e5d38eSCan Guo /** 85803e5d38eSCan Guo * ufshcd_update_tx_eq_params - Update TX Equalization params 85903e5d38eSCan Guo * @params: TX EQ parameters data structure 860adbabdcfSCan Guo * @pwr_mode: target power mode containing gear and rate 86103e5d38eSCan Guo * @eqtr_data: TX EQTR data structure 86203e5d38eSCan Guo * 863adbabdcfSCan Guo * Update TX Equalization params using results from TX EQTR data. Check also 864adbabdcfSCan Guo * the TX EQTR FOM value for each TX lane in the TX EQTR data. If a TX lane got 865adbabdcfSCan Guo * a FOM value of 0, restore the TX Equalization settings from the last known 866adbabdcfSCan Guo * valid TX Equalization params for that specific TX lane. 86703e5d38eSCan Guo */ 86803e5d38eSCan Guo static inline void 86903e5d38eSCan Guo ufshcd_update_tx_eq_params(struct ufshcd_tx_eq_params *params, 870adbabdcfSCan Guo struct ufs_pa_layer_attr *pwr_mode, 87103e5d38eSCan Guo struct ufshcd_tx_eqtr_data *eqtr_data) 87203e5d38eSCan Guo { 87303e5d38eSCan Guo struct ufshcd_tx_eqtr_record *rec = params->eqtr_record; 87403e5d38eSCan Guo 875adbabdcfSCan Guo if (params->is_valid) { 876adbabdcfSCan Guo int lane; 877adbabdcfSCan Guo 878adbabdcfSCan Guo for (lane = 0; lane < pwr_mode->lane_tx; lane++) 879adbabdcfSCan Guo if (eqtr_data->host[lane].fom_val == 0) 880adbabdcfSCan Guo eqtr_data->host[lane] = params->host[lane]; 881adbabdcfSCan Guo 882adbabdcfSCan Guo for (lane = 0; lane < pwr_mode->lane_rx; lane++) 883adbabdcfSCan Guo if (eqtr_data->device[lane].fom_val == 0) 884adbabdcfSCan Guo eqtr_data->device[lane] = params->device[lane]; 885adbabdcfSCan Guo } 886adbabdcfSCan Guo 88703e5d38eSCan Guo memcpy(params->host, eqtr_data->host, sizeof(params->host)); 88803e5d38eSCan Guo memcpy(params->device, eqtr_data->device, sizeof(params->device)); 88903e5d38eSCan Guo 89003e5d38eSCan Guo if (!rec) 89103e5d38eSCan Guo return; 89203e5d38eSCan Guo 89303e5d38eSCan Guo memcpy(rec->host_fom, eqtr_data->host_fom, sizeof(rec->host_fom)); 89403e5d38eSCan Guo memcpy(rec->device_fom, eqtr_data->device_fom, sizeof(rec->device_fom)); 89503e5d38eSCan Guo rec->last_record_ts = ktime_get(); 89603e5d38eSCan Guo rec->last_record_index++; 89703e5d38eSCan Guo } 89803e5d38eSCan Guo 89903e5d38eSCan Guo /** 90003e5d38eSCan Guo * __ufshcd_tx_eqtr - TX Equalization Training (EQTR) procedure 90103e5d38eSCan Guo * @hba: per adapter instance 90203e5d38eSCan Guo * @params: TX EQ parameters data structure 90303e5d38eSCan Guo * @pwr_mode: target power mode containing gear and rate information 90403e5d38eSCan Guo * 90503e5d38eSCan Guo * This function implements the complete TX EQTR procedure as defined in UFSHCI 90603e5d38eSCan Guo * v5.0 specification. It iterates through all possible combinations of PreShoot 90703e5d38eSCan Guo * and DeEmphasis settings to find the optimal TX Equalization settings for all 90803e5d38eSCan Guo * active lanes. 90903e5d38eSCan Guo * 91003e5d38eSCan Guo * Returns 0 on success, negative error code otherwise 91103e5d38eSCan Guo */ 91203e5d38eSCan Guo static int __ufshcd_tx_eqtr(struct ufs_hba *hba, 91303e5d38eSCan Guo struct ufshcd_tx_eq_params *params, 91403e5d38eSCan Guo struct ufs_pa_layer_attr *pwr_mode) 91503e5d38eSCan Guo { 91603e5d38eSCan Guo struct ufshcd_tx_eqtr_data *eqtr_data __free(kfree) = 91703e5d38eSCan Guo kzalloc(sizeof(*eqtr_data), GFP_KERNEL); 91803e5d38eSCan Guo struct tx_eqtr_iter h_iter = {}; 91903e5d38eSCan Guo struct tx_eqtr_iter d_iter = {}; 92003e5d38eSCan Guo u32 gear = pwr_mode->gear_tx; 92103e5d38eSCan Guo u8 preshoot, deemphasis; 92203e5d38eSCan Guo ktime_t start; 92303e5d38eSCan Guo int ret; 92403e5d38eSCan Guo 92503e5d38eSCan Guo if (!eqtr_data) 92603e5d38eSCan Guo return -ENOMEM; 92703e5d38eSCan Guo 92803e5d38eSCan Guo dev_info(hba->dev, "Start TX EQTR procedure for HS-G%u, Rate-%s, RX Lanes: %u, TX Lanes: %u\n", 92903e5d38eSCan Guo gear, ufs_hs_rate_to_str(pwr_mode->hs_rate), 93003e5d38eSCan Guo pwr_mode->lane_rx, pwr_mode->lane_tx); 93103e5d38eSCan Guo 93203e5d38eSCan Guo start = ktime_get(); 93303e5d38eSCan Guo 93403e5d38eSCan Guo /* Step 1 - Determine the TX Adapt Length for EQTR */ 93503e5d38eSCan Guo ret = ufshcd_setup_tx_eqtr_adapt_length(hba, params, gear); 93603e5d38eSCan Guo if (ret) { 93703e5d38eSCan Guo dev_err(hba->dev, "Failed to setup TX EQTR Adaptation length: %d\n", ret); 93803e5d38eSCan Guo return ret; 93903e5d38eSCan Guo } 94003e5d38eSCan Guo 94103e5d38eSCan Guo /* Step 2 - Determine TX Equalization setting capabilities */ 94203e5d38eSCan Guo ret = ufshcd_tx_eqtr_iter_init(hba, &h_iter, &d_iter); 94303e5d38eSCan Guo if (ret) { 94403e5d38eSCan Guo dev_err(hba->dev, "Failed to init TX EQTR data: %d\n", ret); 94503e5d38eSCan Guo return ret; 94603e5d38eSCan Guo } 94703e5d38eSCan Guo 94803e5d38eSCan Guo /* TX EQTR main loop */ 94903e5d38eSCan Guo for (preshoot = 0; preshoot < TX_HS_NUM_PRESHOOT; preshoot++) { 95003e5d38eSCan Guo for (deemphasis = 0; deemphasis < TX_HS_NUM_DEEMPHASIS; deemphasis++) { 95103e5d38eSCan Guo if (!tx_eqtr_iter_update(preshoot, deemphasis, &h_iter, &d_iter)) 95203e5d38eSCan Guo continue; 95303e5d38eSCan Guo 95403e5d38eSCan Guo /* Step 3 - Apply TX EQTR settings */ 95503e5d38eSCan Guo ret = ufshcd_apply_tx_eqtr_settings(hba, pwr_mode, &h_iter, &d_iter); 95603e5d38eSCan Guo if (ret) { 95703e5d38eSCan Guo dev_err(hba->dev, "Failed to apply TX EQTR settings (PreShoot %u, DeEmphasis %u): %d\n", 95803e5d38eSCan Guo preshoot, deemphasis, ret); 95903e5d38eSCan Guo return ret; 96003e5d38eSCan Guo } 96103e5d38eSCan Guo 96203e5d38eSCan Guo /* Step 4 - Trigger UIC TX EQTR */ 96303e5d38eSCan Guo ret = ufshcd_uic_tx_eqtr(hba, gear); 96403e5d38eSCan Guo if (ret) { 96503e5d38eSCan Guo dev_err(hba->dev, "Failed to trigger UIC TX EQTR for target gear %u: %d\n", 96603e5d38eSCan Guo gear, ret); 96703e5d38eSCan Guo return ret; 96803e5d38eSCan Guo } 96903e5d38eSCan Guo 97003e5d38eSCan Guo /* Step 5 - Get FOM */ 97103e5d38eSCan Guo ret = ufshcd_get_rx_fom(hba, pwr_mode, &h_iter, &d_iter); 97203e5d38eSCan Guo if (ret) { 97303e5d38eSCan Guo dev_err(hba->dev, "Failed to get RX_FOM: %d\n", 97403e5d38eSCan Guo ret); 97503e5d38eSCan Guo return ret; 97603e5d38eSCan Guo } 97703e5d38eSCan Guo 97803e5d38eSCan Guo ufshcd_evaluate_tx_eqtr_fom(hba, pwr_mode, eqtr_data, &h_iter, &d_iter); 97903e5d38eSCan Guo } 98003e5d38eSCan Guo } 98103e5d38eSCan Guo 98203e5d38eSCan Guo dev_info(hba->dev, "TX EQTR procedure completed! Time elapsed: %llu ms\n", 98303e5d38eSCan Guo ktime_to_ms(ktime_sub(ktime_get(), start))); 98403e5d38eSCan Guo 985adbabdcfSCan Guo ufshcd_update_tx_eq_params(params, pwr_mode, eqtr_data); 98603e5d38eSCan Guo 98703e5d38eSCan Guo return ret; 98803e5d38eSCan Guo } 98903e5d38eSCan Guo 99003e5d38eSCan Guo /** 99103e5d38eSCan Guo * ufshcd_tx_eqtr_prepare - Prepare UFS link for TX EQTR procedure 99203e5d38eSCan Guo * @hba: per adapter instance 99303e5d38eSCan Guo * @pwr_mode: target power mode containing gear and rate 99403e5d38eSCan Guo * 99503e5d38eSCan Guo * This function prepares the UFS link for TX Equalization Training (EQTR) by 99603e5d38eSCan Guo * establishing the proper initial conditions required by the EQTR procedure. 99703e5d38eSCan Guo * It ensures that EQTR starts from the most reliable Power Mode (HS-G1) with 99803e5d38eSCan Guo * all connected lanes activated and sets host TX HS Adapt Type to INITIAL. 99903e5d38eSCan Guo * 100003e5d38eSCan Guo * Returns 0 on successful preparation, negative error code on failure 100103e5d38eSCan Guo */ 100203e5d38eSCan Guo static int ufshcd_tx_eqtr_prepare(struct ufs_hba *hba, 100303e5d38eSCan Guo struct ufs_pa_layer_attr *pwr_mode) 100403e5d38eSCan Guo { 100503e5d38eSCan Guo struct ufs_pa_layer_attr pwr_mode_hs_g1 = { 100603e5d38eSCan Guo /* TX EQTR shall be initiated from the most reliable HS-G1 */ 100703e5d38eSCan Guo .gear_rx = UFS_HS_G1, 100803e5d38eSCan Guo .gear_tx = UFS_HS_G1, 100903e5d38eSCan Guo .lane_rx = pwr_mode->lane_rx, 101003e5d38eSCan Guo .lane_tx = pwr_mode->lane_tx, 101103e5d38eSCan Guo .pwr_rx = FAST_MODE, 101203e5d38eSCan Guo .pwr_tx = FAST_MODE, 101303e5d38eSCan Guo /* Use the target power mode's HS rate */ 101403e5d38eSCan Guo .hs_rate = pwr_mode->hs_rate, 101503e5d38eSCan Guo }; 101603e5d38eSCan Guo u32 rate = pwr_mode->hs_rate; 101703e5d38eSCan Guo int ret; 101803e5d38eSCan Guo 101903e5d38eSCan Guo /* Change power mode to HS-G1, activate all connected lanes. */ 102003e5d38eSCan Guo ret = ufshcd_change_power_mode(hba, &pwr_mode_hs_g1, 102103e5d38eSCan Guo UFSHCD_PMC_POLICY_DONT_FORCE); 102203e5d38eSCan Guo if (ret) { 102303e5d38eSCan Guo dev_err(hba->dev, "TX EQTR: Failed to change power mode to HS-G1, Rate-%s: %d\n", 102403e5d38eSCan Guo ufs_hs_rate_to_str(rate), ret); 102503e5d38eSCan Guo return ret; 102603e5d38eSCan Guo } 102703e5d38eSCan Guo 102803e5d38eSCan Guo ret = ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXHSADAPTTYPE), 102903e5d38eSCan Guo PA_INITIAL_ADAPT); 103003e5d38eSCan Guo if (ret) 103103e5d38eSCan Guo dev_err(hba->dev, "TX EQTR: Failed to set Host Adapt type to INITIAL: %d\n", 103203e5d38eSCan Guo ret); 103303e5d38eSCan Guo 103403e5d38eSCan Guo return ret; 103503e5d38eSCan Guo } 103603e5d38eSCan Guo 103703e5d38eSCan Guo static void ufshcd_tx_eqtr_unprepare(struct ufs_hba *hba, 103803e5d38eSCan Guo struct ufs_pa_layer_attr *pwr_mode) 103903e5d38eSCan Guo { 104003e5d38eSCan Guo int err; 104103e5d38eSCan Guo 104203e5d38eSCan Guo if (pwr_mode->pwr_rx == SLOWAUTO_MODE || pwr_mode->hs_rate == 0) 104303e5d38eSCan Guo return; 104403e5d38eSCan Guo 104503e5d38eSCan Guo err = ufshcd_change_power_mode(hba, pwr_mode, 104603e5d38eSCan Guo UFSHCD_PMC_POLICY_DONT_FORCE); 104703e5d38eSCan Guo if (err) 104803e5d38eSCan Guo dev_err(hba->dev, "%s: Failed to restore Power Mode: %d\n", 104903e5d38eSCan Guo __func__, err); 105003e5d38eSCan Guo } 105103e5d38eSCan Guo 105203e5d38eSCan Guo /** 105303e5d38eSCan Guo * ufshcd_tx_eqtr - Perform TX EQTR procedures with vops callbacks 105403e5d38eSCan Guo * @hba: per adapter instance 105503e5d38eSCan Guo * @params: TX EQ parameters data structure to populate 105603e5d38eSCan Guo * @pwr_mode: target power mode containing gear and rate information 105703e5d38eSCan Guo * 105803e5d38eSCan Guo * This is the main entry point for performing TX Equalization Training (EQTR) 105903e5d38eSCan Guo * procedure as defined in UFSCHI v5.0 specification. It serves as a wrapper 106003e5d38eSCan Guo * around __ufshcd_tx_eqtr() to provide vops support through the variant 106103e5d38eSCan Guo * operations framework. 106203e5d38eSCan Guo * 106303e5d38eSCan Guo * Returns 0 on success, negative error code on failure 106403e5d38eSCan Guo */ 106503e5d38eSCan Guo static int ufshcd_tx_eqtr(struct ufs_hba *hba, 106603e5d38eSCan Guo struct ufshcd_tx_eq_params *params, 106703e5d38eSCan Guo struct ufs_pa_layer_attr *pwr_mode) 106803e5d38eSCan Guo { 106903e5d38eSCan Guo struct ufs_pa_layer_attr old_pwr_info; 107003e5d38eSCan Guo int ret; 107103e5d38eSCan Guo 107203e5d38eSCan Guo if (!params->eqtr_record) { 107303e5d38eSCan Guo params->eqtr_record = devm_kzalloc(hba->dev, 107403e5d38eSCan Guo sizeof(*params->eqtr_record), 107503e5d38eSCan Guo GFP_KERNEL); 107603e5d38eSCan Guo if (!params->eqtr_record) 107703e5d38eSCan Guo return -ENOMEM; 107803e5d38eSCan Guo } 107903e5d38eSCan Guo 108003e5d38eSCan Guo memcpy(&old_pwr_info, &hba->pwr_info, sizeof(struct ufs_pa_layer_attr)); 108103e5d38eSCan Guo 108203e5d38eSCan Guo ret = ufshcd_tx_eqtr_prepare(hba, pwr_mode); 108303e5d38eSCan Guo if (ret) { 108403e5d38eSCan Guo dev_err(hba->dev, "Failed to prepare TX EQTR: %d\n", ret); 108503e5d38eSCan Guo goto out; 108603e5d38eSCan Guo } 108703e5d38eSCan Guo 108803e5d38eSCan Guo ret = ufshcd_vops_tx_eqtr_notify(hba, PRE_CHANGE, pwr_mode); 108903e5d38eSCan Guo if (ret) 109003e5d38eSCan Guo goto out; 109103e5d38eSCan Guo 109203e5d38eSCan Guo ret = __ufshcd_tx_eqtr(hba, params, pwr_mode); 109303e5d38eSCan Guo if (ret) 109403e5d38eSCan Guo goto out; 109503e5d38eSCan Guo 109603e5d38eSCan Guo ret = ufshcd_vops_tx_eqtr_notify(hba, POST_CHANGE, pwr_mode); 109703e5d38eSCan Guo 109803e5d38eSCan Guo out: 109903e5d38eSCan Guo if (ret) 110003e5d38eSCan Guo ufshcd_tx_eqtr_unprepare(hba, &old_pwr_info); 110103e5d38eSCan Guo 110203e5d38eSCan Guo return ret; 110303e5d38eSCan Guo } 110403e5d38eSCan Guo 110503e5d38eSCan Guo /** 110603e5d38eSCan Guo * ufshcd_config_tx_eq_settings - Configure TX Equalization settings 110703e5d38eSCan Guo * @hba: per adapter instance 110803e5d38eSCan Guo * @pwr_mode: target power mode containing gear and rate information 1109adbabdcfSCan Guo * @force_tx_eqtr: execute the TX EQTR procedure 111003e5d38eSCan Guo * 111103e5d38eSCan Guo * This function finds and sets the TX Equalization settings for the given 111203e5d38eSCan Guo * target power mode. 111303e5d38eSCan Guo * 111403e5d38eSCan Guo * Returns 0 on success, error code otherwise 111503e5d38eSCan Guo */ 111603e5d38eSCan Guo int ufshcd_config_tx_eq_settings(struct ufs_hba *hba, 1117adbabdcfSCan Guo struct ufs_pa_layer_attr *pwr_mode, 1118adbabdcfSCan Guo bool force_tx_eqtr) 111903e5d38eSCan Guo { 112003e5d38eSCan Guo struct ufshcd_tx_eq_params *params; 112103e5d38eSCan Guo u32 gear, rate; 112203e5d38eSCan Guo 112303e5d38eSCan Guo if (!ufshcd_is_tx_eq_supported(hba) || !use_adaptive_txeq) 112403e5d38eSCan Guo return 0; 112503e5d38eSCan Guo 112603e5d38eSCan Guo if (!hba->max_pwr_info.is_valid) { 112703e5d38eSCan Guo dev_err(hba->dev, "Max power info is invalid\n"); 112803e5d38eSCan Guo return -EINVAL; 112903e5d38eSCan Guo } 113003e5d38eSCan Guo 113103e5d38eSCan Guo if (!pwr_mode) { 113203e5d38eSCan Guo dev_err(hba->dev, "Target power mode is NULL\n"); 113303e5d38eSCan Guo return -EINVAL; 113403e5d38eSCan Guo } 113503e5d38eSCan Guo 113603e5d38eSCan Guo gear = pwr_mode->gear_tx; 113703e5d38eSCan Guo rate = pwr_mode->hs_rate; 113803e5d38eSCan Guo 113903e5d38eSCan Guo if (gear < UFS_HS_G1 || gear > UFS_HS_GEAR_MAX) { 114003e5d38eSCan Guo dev_err(hba->dev, "Invalid HS-Gear (%u) for TX Equalization\n", 114103e5d38eSCan Guo gear); 114203e5d38eSCan Guo return -EINVAL; 114303e5d38eSCan Guo } else if (gear < max_t(u32, adaptive_txeq_gear, UFS_HS_G4)) { 114403e5d38eSCan Guo /* TX EQTR is supported for HS-G4 and higher Gears */ 114503e5d38eSCan Guo return 0; 114603e5d38eSCan Guo } 114703e5d38eSCan Guo 114803e5d38eSCan Guo if (rate != PA_HS_MODE_A && rate != PA_HS_MODE_B) { 114903e5d38eSCan Guo dev_err(hba->dev, "Invalid HS-Rate (%u) for TX Equalization\n", 115003e5d38eSCan Guo rate); 115103e5d38eSCan Guo return -EINVAL; 115203e5d38eSCan Guo } 115303e5d38eSCan Guo 115403e5d38eSCan Guo params = &hba->tx_eq_params[gear - 1]; 1155adbabdcfSCan Guo if (!params->is_valid || force_tx_eqtr) { 115603e5d38eSCan Guo int ret; 115703e5d38eSCan Guo 115803e5d38eSCan Guo ret = ufshcd_tx_eqtr(hba, params, pwr_mode); 115903e5d38eSCan Guo if (ret) { 116003e5d38eSCan Guo dev_err(hba->dev, "Failed to train TX Equalization for HS-G%u, Rate-%s: %d\n", 116103e5d38eSCan Guo gear, ufs_hs_rate_to_str(rate), ret); 116203e5d38eSCan Guo return ret; 116303e5d38eSCan Guo } 116403e5d38eSCan Guo 116503e5d38eSCan Guo /* Mark TX Equalization settings as valid */ 116603e5d38eSCan Guo params->is_valid = true; 116703e5d38eSCan Guo params->is_applied = false; 116803e5d38eSCan Guo } 116903e5d38eSCan Guo 117003e5d38eSCan Guo if (params->is_valid && !params->is_applied) { 117103e5d38eSCan Guo int ret; 117203e5d38eSCan Guo 117303e5d38eSCan Guo ret = ufshcd_apply_tx_eq_settings(hba, params, gear); 117403e5d38eSCan Guo if (ret) { 117503e5d38eSCan Guo dev_err(hba->dev, "Failed to apply TX Equalization settings for HS-G%u, Rate-%s: %d\n", 117603e5d38eSCan Guo gear, ufs_hs_rate_to_str(rate), ret); 117703e5d38eSCan Guo return ret; 117803e5d38eSCan Guo } 117903e5d38eSCan Guo 118003e5d38eSCan Guo params->is_applied = true; 118103e5d38eSCan Guo } 118203e5d38eSCan Guo 118303e5d38eSCan Guo return 0; 118403e5d38eSCan Guo } 118503e5d38eSCan Guo 118603e5d38eSCan Guo /** 118703e5d38eSCan Guo * ufshcd_apply_valid_tx_eq_settings - Apply valid TX Equalization settings 118803e5d38eSCan Guo * @hba: per-adapter instance 118903e5d38eSCan Guo * 119003e5d38eSCan Guo * This function iterates through all supported High-Speed (HS) gears and 119103e5d38eSCan Guo * applies valid TX Equalization settings to both Host and Device. 119203e5d38eSCan Guo */ 119303e5d38eSCan Guo void ufshcd_apply_valid_tx_eq_settings(struct ufs_hba *hba) 119403e5d38eSCan Guo { 119503e5d38eSCan Guo struct ufshcd_tx_eq_params *params; 119603e5d38eSCan Guo int gear, err; 119703e5d38eSCan Guo 119803e5d38eSCan Guo if (!ufshcd_is_tx_eq_supported(hba)) 119903e5d38eSCan Guo return; 120003e5d38eSCan Guo 120103e5d38eSCan Guo if (!hba->max_pwr_info.is_valid) { 120203e5d38eSCan Guo dev_err(hba->dev, "Max power info is invalid, cannot apply TX Equalization settings\n"); 120303e5d38eSCan Guo return; 120403e5d38eSCan Guo } 120503e5d38eSCan Guo 120603e5d38eSCan Guo for (gear = UFS_HS_G1; gear <= UFS_HS_GEAR_MAX; gear++) { 120703e5d38eSCan Guo params = &hba->tx_eq_params[gear - 1]; 120803e5d38eSCan Guo 120903e5d38eSCan Guo if (params->is_valid) { 121003e5d38eSCan Guo err = ufshcd_apply_tx_eq_settings(hba, params, gear); 121103e5d38eSCan Guo if (err) { 121203e5d38eSCan Guo params->is_applied = false; 121303e5d38eSCan Guo dev_err(hba->dev, "Failed to apply TX Equalization settings for HS-G%u: %d\n", 121403e5d38eSCan Guo gear, err); 121503e5d38eSCan Guo } else { 121603e5d38eSCan Guo params->is_applied = true; 121703e5d38eSCan Guo } 121803e5d38eSCan Guo } 121903e5d38eSCan Guo } 122003e5d38eSCan Guo } 1221adbabdcfSCan Guo 1222adbabdcfSCan Guo /** 1223adbabdcfSCan Guo * ufshcd_retrain_tx_eq - Retrain TX Equalization and apply new settings 1224adbabdcfSCan Guo * @hba: per-adapter instance 1225adbabdcfSCan Guo * @gear: target High-Speed (HS) gear for retraining 1226adbabdcfSCan Guo * 1227adbabdcfSCan Guo * This function initiates a refresh of the TX Equalization settings for a 1228adbabdcfSCan Guo * specific HS gear. It scales the clocks to maximum frequency, negotiates the 1229adbabdcfSCan Guo * power mode with the device, retrains TX EQ and applies new TX EQ settings 1230adbabdcfSCan Guo * by conducting a Power Mode change. 1231adbabdcfSCan Guo * 1232adbabdcfSCan Guo * Returns 0 on success, non-zero error code otherwise 1233adbabdcfSCan Guo */ 1234adbabdcfSCan Guo int ufshcd_retrain_tx_eq(struct ufs_hba *hba, u32 gear) 1235adbabdcfSCan Guo { 1236adbabdcfSCan Guo struct ufs_pa_layer_attr new_pwr_info, final_params = {}; 1237adbabdcfSCan Guo int ret; 1238adbabdcfSCan Guo 1239adbabdcfSCan Guo if (!ufshcd_is_tx_eq_supported(hba) || !use_adaptive_txeq) 1240adbabdcfSCan Guo return -EOPNOTSUPP; 1241adbabdcfSCan Guo 1242adbabdcfSCan Guo if (gear < adaptive_txeq_gear) 1243adbabdcfSCan Guo return -ERANGE; 1244adbabdcfSCan Guo 1245adbabdcfSCan Guo ufshcd_hold(hba); 1246adbabdcfSCan Guo 1247adbabdcfSCan Guo ret = ufshcd_pause_command_processing(hba, 1 * USEC_PER_SEC); 1248adbabdcfSCan Guo if (ret) { 1249adbabdcfSCan Guo ufshcd_release(hba); 1250adbabdcfSCan Guo return ret; 1251adbabdcfSCan Guo } 1252adbabdcfSCan Guo 1253adbabdcfSCan Guo /* scale up clocks to max frequency before TX EQTR */ 1254adbabdcfSCan Guo if (ufshcd_is_clkscaling_supported(hba)) 1255adbabdcfSCan Guo ufshcd_scale_clks(hba, ULONG_MAX, true); 1256adbabdcfSCan Guo 1257adbabdcfSCan Guo new_pwr_info = hba->pwr_info; 1258adbabdcfSCan Guo new_pwr_info.gear_tx = gear; 1259adbabdcfSCan Guo new_pwr_info.gear_rx = gear; 1260adbabdcfSCan Guo 1261adbabdcfSCan Guo ret = ufshcd_vops_negotiate_pwr_mode(hba, &new_pwr_info, &final_params); 1262adbabdcfSCan Guo if (ret) 1263adbabdcfSCan Guo memcpy(&final_params, &new_pwr_info, sizeof(final_params)); 1264adbabdcfSCan Guo 1265adbabdcfSCan Guo if (final_params.gear_tx != gear) { 1266adbabdcfSCan Guo dev_err(hba->dev, "Negotiated Gear (%u) does not match target Gear (%u)\n", 1267adbabdcfSCan Guo final_params.gear_tx, gear); 1268adbabdcfSCan Guo ret = -EINVAL; 1269adbabdcfSCan Guo goto out; 1270adbabdcfSCan Guo } 1271adbabdcfSCan Guo 1272adbabdcfSCan Guo ret = ufshcd_config_tx_eq_settings(hba, &final_params, true); 1273adbabdcfSCan Guo if (ret) { 1274adbabdcfSCan Guo dev_err(hba->dev, "Failed to config TX Equalization for HS-G%u, Rate-%s: %d\n", 1275adbabdcfSCan Guo final_params.gear_tx, 1276adbabdcfSCan Guo ufs_hs_rate_to_str(final_params.hs_rate), ret); 1277adbabdcfSCan Guo goto out; 1278adbabdcfSCan Guo } 1279adbabdcfSCan Guo 1280adbabdcfSCan Guo /* Change Power Mode to apply the new TX EQ settings */ 1281adbabdcfSCan Guo ret = ufshcd_change_power_mode(hba, &final_params, 1282adbabdcfSCan Guo UFSHCD_PMC_POLICY_FORCE); 1283adbabdcfSCan Guo if (ret) 1284adbabdcfSCan Guo dev_err(hba->dev, "%s: Failed to change Power Mode to HS-G%u, Rate-%s: %d\n", 1285adbabdcfSCan Guo __func__, final_params.gear_tx, 1286adbabdcfSCan Guo ufs_hs_rate_to_str(final_params.hs_rate), ret); 1287adbabdcfSCan Guo 1288adbabdcfSCan Guo out: 1289adbabdcfSCan Guo ufshcd_resume_command_processing(hba); 1290adbabdcfSCan Guo ufshcd_release(hba); 1291adbabdcfSCan Guo 1292adbabdcfSCan Guo return ret; 1293adbabdcfSCan Guo } 1294