diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 9641bcb90cd2..3e57cca1ef33 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2640,6 +2640,42 @@ static int ufshcd_hba_enable(struct ufs_hba *hba) return 0; } +static int ufshcd_disable_tx_lcc(struct ufs_hba *hba, bool peer) +{ + int tx_lanes, i, err = 0; + + if (!peer) + ufshcd_dme_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), + &tx_lanes); + else + ufshcd_dme_peer_get(hba, UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), + &tx_lanes); + for (i = 0; i < tx_lanes; i++) { + if (!peer) + err = ufshcd_dme_set(hba, + UIC_ARG_MIB_SEL(TX_LCC_ENABLE, + UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)), + 0); + else + err = ufshcd_dme_peer_set(hba, + UIC_ARG_MIB_SEL(TX_LCC_ENABLE, + UIC_ARG_MPHY_TX_GEN_SEL_INDEX(i)), + 0); + if (err) { + dev_err(hba->dev, "%s: TX LCC Disable failed, peer = %d, lane = %d, err = %d", + __func__, peer, i, err); + break; + } + } + + return err; +} + +static inline int ufshcd_disable_device_tx_lcc(struct ufs_hba *hba) +{ + return ufshcd_disable_tx_lcc(hba, true); +} + /** * ufshcd_link_startup - Initialize unipro link startup * @hba: per adapter instance @@ -2677,6 +2713,12 @@ static int ufshcd_link_startup(struct ufs_hba *hba) /* failed to get the link up... retire */ goto out; + if (hba->quirks & UFSHCD_QUIRK_BROKEN_LCC) { + ret = ufshcd_disable_device_tx_lcc(hba); + if (ret) + goto out; + } + /* Include any host controller configuration via UIC commands */ if (hba->vops && hba->vops->link_startup_notify) { ret = hba->vops->link_startup_notify(hba, POST_CHANGE); diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index fc8bec9960b1..b845f1535f29 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -426,6 +426,14 @@ struct ufs_hba { */ #define UFSHCD_QUIRK_DELAY_BEFORE_DME_CMDS UFS_BIT(1) + /* + * If UFS host controller is having issue in processing LCC (Line + * Control Command) coming from device then enable this quirk. + * When this quirk is enabled, host controller driver should disable + * the LCC transmission on UFS device (by clearing TX_LCC_ENABLE + * attribute of device to 0). + */ + #define UFSHCD_QUIRK_BROKEN_LCC UFS_BIT(2) unsigned int quirks; /* Deviations from standard UFSHCI spec. */ diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h index d5721199e9cc..f8909ece2171 100644 --- a/drivers/scsi/ufs/ufshci.h +++ b/drivers/scsi/ufs/ufshci.h @@ -206,6 +206,9 @@ enum { #define CONFIG_RESULT_CODE_MASK 0xFF #define GENERIC_ERROR_CODE_MASK 0xFF +/* GenSelectorIndex calculation macros for M-PHY attributes */ +#define UIC_ARG_MPHY_TX_GEN_SEL_INDEX(lane) (lane) + #define UIC_ARG_MIB_SEL(attr, sel) ((((attr) & 0xFFFF) << 16) |\ ((sel) & 0xFFFF)) #define UIC_ARG_MIB(attr) UIC_ARG_MIB_SEL(attr, 0)