* 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>
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;
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,
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)
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));
}
/**
{
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);
*/
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)
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,
{
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;
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;
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);