From 5dab730cac673c635a5107a28c0c4288a442f135 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 16 Aug 2023 17:05:49 +0200 Subject: [PATCH 1/7] ufs: Convert quirks to BIT() macro Use BIT() macro for quirks, no functional change. Signed-off-by: Marek Vasut Reviewed-by: Bhupesh Sharma --- drivers/ufs/ufs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h index 8a38832b05f..5a5c13aefdf 100644 --- a/drivers/ufs/ufs.h +++ b/drivers/ufs/ufs.h @@ -717,7 +717,7 @@ struct ufs_hba { * the LCC transmission on UFS device (by clearing TX_LCC_ENABLE * attribute of device to 0). */ -#define UFSHCD_QUIRK_BROKEN_LCC 0x1 +#define UFSHCD_QUIRK_BROKEN_LCC BIT(0) /* Virtual memory reference */ struct utp_transfer_cmd_desc *ucdl; From 91913a1aa2ffeb7278d7d4e4bc51565bb38cebeb Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 16 Aug 2023 17:05:50 +0200 Subject: [PATCH 2/7] ufs: Add UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS Add UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS for host controllers which do not support 64-bit addressing. Ported from Linux kernel commit 6554400d6f66 ("scsi: ufs: core: Add UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS") with ufs_scsi_buffer_aligned() based on U-Boot generic bounce buffer. Signed-off-by: Marek Vasut Reviewed-by: Bhupesh Sharma Tested-by: Bhupesh Sharma --- drivers/ufs/ufs.c | 26 ++++++++++++++++++++++++++ drivers/ufs/ufs.h | 6 ++++++ 2 files changed, 32 insertions(+) diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c index 3bf1a95e7f2..da0550d98c6 100644 --- a/drivers/ufs/ufs.c +++ b/drivers/ufs/ufs.c @@ -8,6 +8,7 @@ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com */ +#include #include #include #include @@ -1889,6 +1890,8 @@ int ufshcd_probe(struct udevice *ufs_dev, struct ufs_hba_ops *hba_ops) /* Read capabilties registers */ hba->capabilities = ufshcd_readl(hba, REG_CONTROLLER_CAPABILITIES); + if (hba->quirks & UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS) + hba->capabilities &= ~MASK_64_ADDRESSING_SUPPORT; /* Get UFS version supported by the controller */ hba->version = ufshcd_get_ufs_version(hba); @@ -1942,8 +1945,31 @@ int ufs_scsi_bind(struct udevice *ufs_dev, struct udevice **scsi_devp) return ret; } +#if IS_ENABLED(CONFIG_BOUNCE_BUFFER) +static int ufs_scsi_buffer_aligned(struct udevice *scsi_dev, struct bounce_buffer *state) +{ +#ifdef CONFIG_PHYS_64BIT + struct ufs_hba *hba = dev_get_uclass_priv(scsi_dev->parent); + uintptr_t ubuf = (uintptr_t)state->user_buffer; + size_t len = state->len_aligned; + + /* Check if below 32bit boundary */ + if ((hba->quirks & UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS) && + ((ubuf >> 32) || (ubuf + len) >> 32)) { + dev_dbg(scsi_dev, "Buffer above 32bit boundary %lx-%lx\n", + ubuf, ubuf + len); + return 0; + } +#endif + return 1; +} +#endif /* CONFIG_BOUNCE_BUFFER */ + static struct scsi_ops ufs_ops = { .exec = ufs_scsi_exec, +#if IS_ENABLED(CONFIG_BOUNCE_BUFFER) + .buffer_aligned = ufs_scsi_buffer_aligned, +#endif /* CONFIG_BOUNCE_BUFFER */ }; int ufs_probe_dev(int index) diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h index 5a5c13aefdf..e5ddb6f64a9 100644 --- a/drivers/ufs/ufs.h +++ b/drivers/ufs/ufs.h @@ -719,6 +719,12 @@ struct ufs_hba { */ #define UFSHCD_QUIRK_BROKEN_LCC BIT(0) +/* + * This quirk needs to be enabled if the host controller has + * 64-bit addressing supported capability but it doesn't work. + */ +#define UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS BIT(1) + /* Virtual memory reference */ struct utp_transfer_cmd_desc *ucdl; struct utp_transfer_req_desc *utrdl; From f430151e105b055abd687a456c3980b3674eeb42 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 16 Aug 2023 17:05:51 +0200 Subject: [PATCH 3/7] ufs: Add UFSHCD_QUIRK_HIBERN_FASTAUTO Add UFSHCD_QUIRK_HIBERN_FASTAUTO quirk for host controllers which supports auto-hibernate the capability but only FASTAUTO mode. Ported from Linux kernel commit 2f11bbc2c7f3 ("scsi: ufs: core: Add UFSHCD_QUIRK_HIBERN_FASTAUTO") Signed-off-by: Marek Vasut Reviewed-by: Bhupesh Sharma Tested-by: Bhupesh Sharma --- drivers/ufs/ufs.c | 9 +++++++-- drivers/ufs/ufs.h | 6 ++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c index da0550d98c6..261ae2843c2 100644 --- a/drivers/ufs/ufs.c +++ b/drivers/ufs/ufs.c @@ -1631,8 +1631,13 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba) if (hba->max_pwr_info.is_valid) return 0; - pwr_info->pwr_tx = FAST_MODE; - pwr_info->pwr_rx = FAST_MODE; + if (hba->quirks & UFSHCD_QUIRK_HIBERN_FASTAUTO) { + pwr_info->pwr_tx = FASTAUTO_MODE; + pwr_info->pwr_rx = FASTAUTO_MODE; + } else { + pwr_info->pwr_tx = FAST_MODE; + pwr_info->pwr_rx = FAST_MODE; + } pwr_info->hs_rate = PA_HS_MODE_B; /* Get the connected lane count */ diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h index e5ddb6f64a9..638c10b5503 100644 --- a/drivers/ufs/ufs.h +++ b/drivers/ufs/ufs.h @@ -725,6 +725,12 @@ struct ufs_hba { */ #define UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS BIT(1) +/* + * This quirk needs to be enabled if the host controller has + * auto-hibernate capability but it's FASTAUTO only. + */ +#define UFSHCD_QUIRK_HIBERN_FASTAUTO BIT(2) + /* Virtual memory reference */ struct utp_transfer_cmd_desc *ucdl; struct utp_transfer_req_desc *utrdl; From 2ff810ae5e823ba42c7b543b06d87e3dd7d1cb81 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 16 Aug 2023 17:05:52 +0200 Subject: [PATCH 4/7] ufs: Handle UFS 3.0 controllers Extend the version check to handle UFS 3.0 controllers as well. Tested on R-Car S4 UFS 3.0 controller. Signed-off-by: Marek Vasut Reviewed-by: Bhupesh Sharma Tested-by: Bhupesh Sharma --- drivers/ufs/ufs.c | 3 ++- drivers/ufs/ufs.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c index 261ae2843c2..58830c8ddca 100644 --- a/drivers/ufs/ufs.c +++ b/drivers/ufs/ufs.c @@ -1903,7 +1903,8 @@ int ufshcd_probe(struct udevice *ufs_dev, struct ufs_hba_ops *hba_ops) if (hba->version != UFSHCI_VERSION_10 && hba->version != UFSHCI_VERSION_11 && hba->version != UFSHCI_VERSION_20 && - hba->version != UFSHCI_VERSION_21) + hba->version != UFSHCI_VERSION_21 && + hba->version != UFSHCI_VERSION_30) dev_err(hba->dev, "invalid UFS version 0x%x\n", hba->version); diff --git a/drivers/ufs/ufs.h b/drivers/ufs/ufs.h index 638c10b5503..9daaf03d222 100644 --- a/drivers/ufs/ufs.h +++ b/drivers/ufs/ufs.h @@ -781,6 +781,7 @@ enum { UFSHCI_VERSION_11 = 0x00010100, /* 1.1 */ UFSHCI_VERSION_20 = 0x00000200, /* 2.0 */ UFSHCI_VERSION_21 = 0x00000210, /* 2.1 */ + UFSHCI_VERSION_30 = 0x00000300, /* 3.0 */ }; /* Interrupt disable masks */ From 7f26fcbea82caa57852fc93bee7ac7300a42c730 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 16 Aug 2023 17:05:53 +0200 Subject: [PATCH 5/7] ufs: Pass hba pointer to ufshcd_prepare_req_desc_hdr() Pass the hba pointer itself to ufshcd_prepare_req_desc_hdr() instead of duplicating utp_transfer_req_desc access at each call site. No functional change. Signed-off-by: Marek Vasut Reviewed-by: Bhupesh Sharma Tested-by: Bhupesh Sharma --- drivers/ufs/ufs.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c index 58830c8ddca..da1009e2c14 100644 --- a/drivers/ufs/ufs.c +++ b/drivers/ufs/ufs.c @@ -696,10 +696,11 @@ static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba) * ufshcd_prepare_req_desc_hdr() - Fills the requests header * descriptor according to request */ -static void ufshcd_prepare_req_desc_hdr(struct utp_transfer_req_desc *req_desc, +static void ufshcd_prepare_req_desc_hdr(struct ufs_hba *hba, u32 *upiu_flags, enum dma_data_direction cmd_dir) { + struct utp_transfer_req_desc *req_desc = hba->utrdl; u32 data_direction; u32 dword_0; @@ -793,11 +794,10 @@ static int ufshcd_comp_devman_upiu(struct ufs_hba *hba, { u32 upiu_flags; int ret = 0; - struct utp_transfer_req_desc *req_desc = hba->utrdl; hba->dev_cmd.type = cmd_type; - ufshcd_prepare_req_desc_hdr(req_desc, &upiu_flags, DMA_NONE); + ufshcd_prepare_req_desc_hdr(hba, &upiu_flags, DMA_NONE); switch (cmd_type) { case DEV_CMD_TYPE_QUERY: ufshcd_prepare_utp_query_req_upiu(hba, upiu_flags); @@ -1449,12 +1449,11 @@ static void prepare_prdt_table(struct ufs_hba *hba, struct scsi_cmd *pccb) static int ufs_scsi_exec(struct udevice *scsi_dev, struct scsi_cmd *pccb) { struct ufs_hba *hba = dev_get_uclass_priv(scsi_dev->parent); - struct utp_transfer_req_desc *req_desc = hba->utrdl; u32 upiu_flags; int ocs, result = 0; u8 scsi_status; - ufshcd_prepare_req_desc_hdr(req_desc, &upiu_flags, pccb->dma_dir); + ufshcd_prepare_req_desc_hdr(hba, &upiu_flags, pccb->dma_dir); ufshcd_prepare_utp_scsi_cmd_upiu(hba, pccb, upiu_flags); prepare_prdt_table(hba, pccb); From 12675cb100a860ec7f8fe6df36bb0f7602797e29 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 16 Aug 2023 17:05:54 +0200 Subject: [PATCH 6/7] ufs: Use utp_transfer_req_desc pointer in ufshcd_get_tr_ocs Use utp_transfer_req_desc pointer to reference to utrdl queue instead of referencing the queue directly. This makes the code more consistent. No functional change. Signed-off-by: Marek Vasut Reviewed-by: Bhupesh Sharma Tested-by: Bhupesh Sharma --- drivers/ufs/ufs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c index da1009e2c14..041caee714f 100644 --- a/drivers/ufs/ufs.c +++ b/drivers/ufs/ufs.c @@ -858,7 +858,9 @@ static inline int ufshcd_get_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr) */ static inline int ufshcd_get_tr_ocs(struct ufs_hba *hba) { - return le32_to_cpu(hba->utrdl->header.dword_2) & MASK_OCS; + struct utp_transfer_req_desc *req_desc = hba->utrdl; + + return le32_to_cpu(req_desc->header.dword_2) & MASK_OCS; } static inline int ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr) From c5b3e5cd3d573bd3615c09f1aaa19291608e2093 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Wed, 16 Aug 2023 17:05:55 +0200 Subject: [PATCH 7/7] ufs: Implement cache management Add function to flush and invalidate cache over request and response queue entries, and perform flush and optional invalidate over block layer data that are passed into the UFS layer. This makes it possible to use UFS with caches enabled. Signed-off-by: Marek Vasut Reviewed-by: Bhupesh Sharma Tested-by: Bhupesh Sharma --- drivers/ufs/ufs.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c index 041caee714f..7c48d57f99d 100644 --- a/drivers/ufs/ufs.c +++ b/drivers/ufs/ufs.c @@ -692,6 +692,21 @@ static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba) return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) >> 8) & 0x7; } +/** + * ufshcd_cache_flush_and_invalidate - Flush and invalidate cache + * + * Flush and invalidate cache in aligned address..address+size range. + * The invalidation is in place to avoid stale data in cache. + */ +static void ufshcd_cache_flush_and_invalidate(void *addr, unsigned long size) +{ + uintptr_t aaddr = (uintptr_t)addr & ~(ARCH_DMA_MINALIGN - 1); + unsigned long asize = ALIGN(size, ARCH_DMA_MINALIGN); + + flush_dcache_range(aaddr, aaddr + asize); + invalidate_dcache_range(aaddr, aaddr + asize); +} + /** * ufshcd_prepare_req_desc_hdr() - Fills the requests header * descriptor according to request @@ -735,6 +750,8 @@ static void ufshcd_prepare_req_desc_hdr(struct ufs_hba *hba, req_desc->header.dword_3 = 0; req_desc->prd_table_length = 0; + + ufshcd_cache_flush_and_invalidate(req_desc, sizeof(*req_desc)); } static void ufshcd_prepare_utp_query_req_upiu(struct ufs_hba *hba, @@ -763,10 +780,15 @@ static void ufshcd_prepare_utp_query_req_upiu(struct ufs_hba *hba, memcpy(&ucd_req_ptr->qr, &query->request.upiu_req, QUERY_OSF_SIZE); /* Copy the Descriptor */ - if (query->request.upiu_req.opcode == UPIU_QUERY_OPCODE_WRITE_DESC) + if (query->request.upiu_req.opcode == UPIU_QUERY_OPCODE_WRITE_DESC) { memcpy(ucd_req_ptr + 1, query->descriptor, len); + ufshcd_cache_flush_and_invalidate(ucd_req_ptr, 2 * sizeof(*ucd_req_ptr)); + } else { + ufshcd_cache_flush_and_invalidate(ucd_req_ptr, sizeof(*ucd_req_ptr)); + } memset(hba->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp)); + ufshcd_cache_flush_and_invalidate(hba->ucd_rsp_ptr, sizeof(*hba->ucd_rsp_ptr)); } static inline void ufshcd_prepare_utp_nop_upiu(struct ufs_hba *hba) @@ -783,6 +805,9 @@ static inline void ufshcd_prepare_utp_nop_upiu(struct ufs_hba *hba) ucd_req_ptr->header.dword_2 = 0; memset(hba->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp)); + + ufshcd_cache_flush_and_invalidate(ucd_req_ptr, sizeof(*ucd_req_ptr)); + ufshcd_cache_flush_and_invalidate(hba->ucd_rsp_ptr, sizeof(*hba->ucd_rsp_ptr)); } /** @@ -1409,6 +1434,8 @@ void ufshcd_prepare_utp_scsi_cmd_upiu(struct ufs_hba *hba, memcpy(ucd_req_ptr->sc.cdb, pccb->cmd, cdb_len); memset(hba->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp)); + ufshcd_cache_flush_and_invalidate(ucd_req_ptr, sizeof(*ucd_req_ptr)); + ufshcd_cache_flush_and_invalidate(hba->ucd_rsp_ptr, sizeof(*hba->ucd_rsp_ptr)); } static inline void prepare_prdt_desc(struct ufshcd_sg_entry *entry, @@ -1423,6 +1450,7 @@ static void prepare_prdt_table(struct ufs_hba *hba, struct scsi_cmd *pccb) { struct utp_transfer_req_desc *req_desc = hba->utrdl; struct ufshcd_sg_entry *prd_table = hba->ucd_prdt_ptr; + uintptr_t aaddr = (uintptr_t)(pccb->pdata) & ~(ARCH_DMA_MINALIGN - 1); ulong datalen = pccb->datalen; int table_length; u8 *buf; @@ -1430,9 +1458,19 @@ static void prepare_prdt_table(struct ufs_hba *hba, struct scsi_cmd *pccb) if (!datalen) { req_desc->prd_table_length = 0; + ufshcd_cache_flush_and_invalidate(req_desc, sizeof(*req_desc)); return; } + if (pccb->dma_dir == DMA_TO_DEVICE) { /* Write to device */ + flush_dcache_range(aaddr, aaddr + + ALIGN(datalen, ARCH_DMA_MINALIGN)); + } + + /* In any case, invalidate cache to avoid stale data in it. */ + invalidate_dcache_range(aaddr, aaddr + + ALIGN(datalen, ARCH_DMA_MINALIGN)); + table_length = DIV_ROUND_UP(pccb->datalen, MAX_PRDT_ENTRY); buf = pccb->pdata; i = table_length; @@ -1446,6 +1484,8 @@ static void prepare_prdt_table(struct ufs_hba *hba, struct scsi_cmd *pccb) prepare_prdt_desc(&prd_table[table_length - i - 1], buf, datalen - 1); req_desc->prd_table_length = table_length; + ufshcd_cache_flush_and_invalidate(prd_table, sizeof(*prd_table) * table_length); + ufshcd_cache_flush_and_invalidate(req_desc, sizeof(*req_desc)); } static int ufs_scsi_exec(struct udevice *scsi_dev, struct scsi_cmd *pccb)