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 <mrana@codeaurora.org>
This commit is contained in:
Mayank Rana 2021-04-19 16:10:01 -07:00
commit e334d6d2ed
2 changed files with 119 additions and 24 deletions

View file

@ -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.

View file

@ -8,6 +8,7 @@
#include <linux/scatterlist.h>
#include <linux/usb/gadget.h>
#include <linux/soc/qcom/llcc-tcm.h>
/* 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;
};
/*