From e334d6d2edc3da24d391beac4b4643559f206f2c Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Mon, 19 Apr 2021 16:10:01 -0700 Subject: [PATCH] dwc3-msm: Add LLCC TCM memory usage for USB IN endpoint data buffers Add LLCC TCM memory usage for USB IN endpoint data buffers. Change-Id: Iab4153838684530736bfb7373ecb492ca4202f08 Signed-off-by: Mayank Rana --- drivers/usb/dwc3/dwc3-msm.c | 140 +++++++++++++++++++++++++++++------ include/linux/usb/dwc3-msm.h | 3 + 2 files changed, 119 insertions(+), 24 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index 95e826b1edba..f9e78b57fd19 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -1447,6 +1447,112 @@ static int gsi_updatexfer_for_ep(struct usb_ep *ep, return ret; } +#define TCM_BUF_SIZE (16 * 1024) +#define TCM_BUF_NUM 8 +#define TCM_MEM_REQ (TCM_BUF_SIZE * TCM_BUF_NUM) + +/* Using IOVA base address at end of USB IOVA address */ +#define TCM_IOVA_BASE 0x9ffe0000 +static void gsi_free_data_buffers(struct usb_ep *ep, + struct usb_gsi_request *req) +{ + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + + if (req->tcm_mem) { + dbg_log_string("free tcm based buffer for ep:%s\n", dep->name); + iommu_unmap(iommu_get_domain_for_dev(dwc->sysdev), + TCM_IOVA_BASE, TCM_MEM_REQ); + llcc_tcm_deactivate(req->tcm_mem); + req->tcm_mem = NULL; + req->dma = 0; + } else { + dma_free_coherent(dwc->sysdev, + req->buf_len * req->num_bufs, + req->buf_base_addr, req->dma); + } + req->buf_base_addr = NULL; + sg_free_table(&req->sgt_data_buff); +} + +static int gsi_allocate_data_buffers(struct usb_ep *ep, + struct usb_gsi_request *req) +{ + size_t len = 0; + int ret = 0; + struct dwc3_ep *dep = to_dwc3_ep(ep); + struct dwc3 *dwc = dep->dwc; + struct iommu_domain *usb_domain = NULL; + + /* Check need of using tcm for IN endpoint only */ + if (!(req->use_tcm_mem && dep->direction)) + goto normal_alloc; + + usb_domain = iommu_get_domain_for_dev(dwc->sysdev); + if (usb_domain == NULL) { + dev_err(dwc->dev, "No USB IOMMU domain\n"); + goto normal_alloc; + } + + /* Validate USB IOVA range with TCM IOVA base */ + if (usb_domain->geometry.aperture_start <= TCM_IOVA_BASE && + usb_domain->geometry.aperture_end > TCM_IOVA_BASE) { + dev_err(dwc->dev, "overlap IOVA: TCM:%x USB:(%x-%x)\n", + TCM_IOVA_BASE, + usb_domain->geometry.aperture_start, + usb_domain->geometry.aperture_end); + goto normal_alloc; + } + + /* Check availability of TCM memory */ + req->tcm_mem = llcc_tcm_activate(); + if (IS_ERR_OR_NULL(req->tcm_mem)) { + dev_err(dwc->dev, "can't use tcm_mem ep:%s err:%ld\n", + dep->name, PTR_ERR(req->tcm_mem)); + req->tcm_mem = NULL; + goto normal_alloc; + } + + if (req->tcm_mem->mem_size < TCM_MEM_REQ) { + dev_err(dwc->dev, "err:tcm_mem: req sz:(%d) > avail_sz(%d)\n", + TCM_MEM_REQ, req->tcm_mem->mem_size); + llcc_tcm_deactivate(req->tcm_mem); + req->tcm_mem = NULL; + goto normal_alloc; + } + + len = TCM_MEM_REQ; + ret = iommu_map(usb_domain, TCM_IOVA_BASE, req->tcm_mem->phys_addr, len, + IOMMU_READ | IOMMU_WRITE | IOMMU_NOEXEC); + if (ret) { + dev_err(dwc->dev, "%s: can't map TCM mem, using DDR\n", + dep->name); + llcc_tcm_deactivate(req->tcm_mem); + req->tcm_mem = NULL; + goto normal_alloc; + } + + req->buf_base_addr = (void __force *)req->tcm_mem->virt_addr; + req->buf_len = TCM_BUF_SIZE; + req->num_bufs = TCM_BUF_NUM; + req->dma = TCM_IOVA_BASE; + goto fill_sgtable; + +normal_alloc: + len = req->buf_len * req->num_bufs; + req->buf_base_addr = dma_alloc_coherent(dwc->sysdev, len, + &req->dma, GFP_KERNEL); + if (!req->buf_base_addr) + return -ENOMEM; + +fill_sgtable: + dma_get_sgtable(dwc->sysdev, &req->sgt_data_buff, + req->buf_base_addr, req->dma, len); + dbg_log_string("alloc buffer for ep:%s use_tcm_mem:%d mem_type:%s\n", + dep->name, req->use_tcm_mem, req->tcm_mem ? "TCM" : "DDR"); + return 0; +} + /** * Allocates Buffers and TRBs. Configures TRBs for GSI EPs. * @@ -1457,39 +1563,30 @@ static int gsi_updatexfer_for_ep(struct usb_ep *ep, */ static int gsi_prepare_trbs(struct usb_ep *ep, struct usb_gsi_request *req) { - int i = 0; - size_t len; + int i = 0, ret; dma_addr_t buffer_addr; dma_addr_t trb0_dma; struct dwc3_ep *dep = to_dwc3_ep(ep); struct dwc3 *dwc = dep->dwc; struct dwc3_trb *trb; - int num_trbs = (dep->direction) ? (2 * (req->num_bufs) + 2) - : (req->num_bufs + 2); + int num_trbs; struct scatterlist *sg; struct sg_table *sgt; - /* Allocate TRB buffers */ - - len = req->buf_len * req->num_bufs; - req->buf_base_addr = dma_alloc_coherent(dwc->sysdev, len, &req->dma, - GFP_KERNEL); - if (!req->buf_base_addr) { - dev_err(dwc->dev, "buf_base_addr allocate failed %s\n", + ret = gsi_allocate_data_buffers(ep, req); + if (ret) { + dev_err(dep->dwc->dev, "failed to alloc TRB buffer for %s\n", dep->name); - return -ENOMEM; + return ret; } - dma_get_sgtable(dwc->sysdev, &req->sgt_data_buff, req->buf_base_addr, - req->dma, len); - buffer_addr = req->dma; - /* Allocate and configure TRBs */ + num_trbs = (dep->direction) ? (2 * (req->num_bufs) + 2) + : (req->num_bufs + 2); dep->trb_pool = dma_alloc_coherent(dwc->sysdev, num_trbs * sizeof(struct dwc3_trb), &dep->trb_pool_dma, GFP_KERNEL); - if (!dep->trb_pool) { dev_err(dep->dwc->dev, "failed to alloc trb dma pool for %s\n", dep->name); @@ -1564,9 +1661,7 @@ static int gsi_prepare_trbs(struct usb_ep *ep, struct usb_gsi_request *req) return 0; free_trb_buffer: - dma_free_coherent(dwc->sysdev, len, req->buf_base_addr, req->dma); - req->buf_base_addr = NULL; - sg_free_table(&req->sgt_data_buff); + gsi_free_data_buffers(ep, req); return -ENOMEM; } @@ -1596,10 +1691,7 @@ static void gsi_free_trbs(struct usb_ep *ep, struct usb_gsi_request *req) sg_free_table(&req->sgt_trb_xfer_ring); /* free TRB buffers */ - dma_free_coherent(dwc->sysdev, req->buf_len * req->num_bufs, - req->buf_base_addr, req->dma); - req->buf_base_addr = NULL; - sg_free_table(&req->sgt_data_buff); + gsi_free_data_buffers(ep, req); } /** * Configures GSI EPs. For GSI EPs we need to set interrupter numbers. diff --git a/include/linux/usb/dwc3-msm.h b/include/linux/usb/dwc3-msm.h index 7a800bfb3420..f3739a65ea0e 100644 --- a/include/linux/usb/dwc3-msm.h +++ b/include/linux/usb/dwc3-msm.h @@ -8,6 +8,7 @@ #include #include +#include /* used for struct usb_phy flags */ #define PHY_HOST_MODE BIT(0) @@ -85,6 +86,8 @@ struct usb_gsi_request { struct sg_table sgt_trb_xfer_ring; struct sg_table sgt_data_buff; struct device *dev; + bool use_tcm_mem; + struct llcc_tcm_data *tcm_mem; }; /*