]> git.ipfire.org Git - thirdparty/u-boot.git/blobdiff - drivers/ufs/ufs.c
tree-wide: Replace http:// link with https:// link for ti.com
[thirdparty/u-boot.git] / drivers / ufs / ufs.c
index 58830c8ddcacd1226a74364a3e3f704ea0643ddc..346f0fd916f1ac5dafae634024f65936e8fbbd16 100644 (file)
@@ -5,7 +5,7 @@
  * Taken from Linux Kernel v5.2 (drivers/scsi/ufs/ufshcd.c) and ported
  * to u-boot.
  *
- * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2019 Texas Instruments Incorporated - https://www.ti.com
  */
 
 #include <bouncebuf.h>
@@ -692,14 +692,30 @@ 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
  */
-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;
 
@@ -734,6 +750,8 @@ static void ufshcd_prepare_req_desc_hdr(struct utp_transfer_req_desc *req_desc,
        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,
@@ -762,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)
@@ -782,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));
 }
 
 /**
@@ -793,11 +819,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);
@@ -858,7 +883,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)
@@ -1407,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,
@@ -1421,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;
@@ -1428,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;
@@ -1444,17 +1484,18 @@ 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)
 {
        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);