From: Alim Akhtar alim.akhtar@samsung.com
[ Upstream commit 26f968d7de823ba4974a8f25c8bd8ee2df6ab74b ]
Some UFS host controllers like Exynos uses granularities of PRDT length and offset as bytes, whereas others use actual segment count.
Link: https://lore.kernel.org/r/20200528011658.71590-5-alim.akhtar@samsung.com Reviewed-by: Avri Altman avri.altman@wdc.com Signed-off-by: Kiwoong Kim kwmad.kim@samsung.com Signed-off-by: Alim Akhtar alim.akhtar@samsung.com Signed-off-by: Martin K. Petersen martin.petersen@oracle.com Signed-off-by: Sasha Levin sashal@kernel.org --- drivers/scsi/ufs/ufshcd.c | 30 +++++++++++++++++++++++------- drivers/scsi/ufs/ufshcd.h | 6 ++++++ 2 files changed, 29 insertions(+), 7 deletions(-)
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 87473fa5bd0f9..0be06e7c5f293 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -2175,8 +2175,14 @@ static int ufshcd_map_sg(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) return sg_segments;
if (sg_segments) { - lrbp->utr_descriptor_ptr->prd_table_length = - cpu_to_le16((u16)sg_segments); + + if (hba->quirks & UFSHCD_QUIRK_PRDT_BYTE_GRAN) + lrbp->utr_descriptor_ptr->prd_table_length = + cpu_to_le16((sg_segments * + sizeof(struct ufshcd_sg_entry))); + else + lrbp->utr_descriptor_ptr->prd_table_length = + cpu_to_le16((u16) (sg_segments));
prd_table = (struct ufshcd_sg_entry *)lrbp->ucd_prdt_ptr;
@@ -3523,11 +3529,21 @@ static void ufshcd_host_memory_configure(struct ufs_hba *hba) cpu_to_le32(upper_32_bits(cmd_desc_element_addr));
/* Response upiu and prdt offset should be in double words */ - utrdlp[i].response_upiu_offset = - cpu_to_le16(response_offset >> 2); - utrdlp[i].prd_table_offset = cpu_to_le16(prdt_offset >> 2); - utrdlp[i].response_upiu_length = - cpu_to_le16(ALIGNED_UPIU_SIZE >> 2); + if (hba->quirks & UFSHCD_QUIRK_PRDT_BYTE_GRAN) { + utrdlp[i].response_upiu_offset = + cpu_to_le16(response_offset); + utrdlp[i].prd_table_offset = + cpu_to_le16(prdt_offset); + utrdlp[i].response_upiu_length = + cpu_to_le16(ALIGNED_UPIU_SIZE); + } else { + utrdlp[i].response_upiu_offset = + cpu_to_le16(response_offset >> 2); + utrdlp[i].prd_table_offset = + cpu_to_le16(prdt_offset >> 2); + utrdlp[i].response_upiu_length = + cpu_to_le16(ALIGNED_UPIU_SIZE >> 2); + }
ufshcd_init_lrb(hba, &hba->lrb[i], i); } diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 4198e5d883a1a..97d649f546e3a 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -537,6 +537,12 @@ enum ufshcd_quirks { * enabled via HCE register. */ UFSHCI_QUIRK_BROKEN_HCE = 1 << 8, + + /* + * This quirk needs to be enabled if the host controller regards + * resolution of the values of PRDTO and PRDTL in UTRD as byte. + */ + UFSHCD_QUIRK_PRDT_BYTE_GRAN = 1 << 9, };
enum ufshcd_caps {