From patchwork Mon Jan 21 13:44:47 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Marek Szyprowski X-Patchwork-Id: 10773897 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A3C87913 for ; Mon, 21 Jan 2019 13:44:58 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 92F262A14B for ; Mon, 21 Jan 2019 13:44:58 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 90CA92A15E; Mon, 21 Jan 2019 13:44:58 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 9A50A2A154 for ; Mon, 21 Jan 2019 13:44:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729059AbfAUNo4 (ORCPT ); Mon, 21 Jan 2019 08:44:56 -0500 Received: from mailout1.w1.samsung.com ([210.118.77.11]:46245 "EHLO mailout1.w1.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728801AbfAUNoz (ORCPT ); Mon, 21 Jan 2019 08:44:55 -0500 Received: from eucas1p2.samsung.com (unknown [182.198.249.207]) by mailout1.w1.samsung.com (KnoxPortal) with ESMTP id 20190121134453euoutp0103980232925672e3f8127c9cc3d8fc04~74QF1JChW0547805478euoutp01c; Mon, 21 Jan 2019 13:44:53 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.w1.samsung.com 20190121134453euoutp0103980232925672e3f8127c9cc3d8fc04~74QF1JChW0547805478euoutp01c DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1548078293; bh=gM6h9JerxRBB4eT1PmZpr9f9za3+A4G4TsHpdhn3XCM=; h=From:To:Cc:Subject:Date:In-reply-to:References:From; b=qxFHXF4ybaW4rmc5m9ezpeRNk734KHFWwNhrZyK5VP6GCgKrZF/gxdCRGevsBWjip aPbmwwpBjuOue1l1rOHFlNXS6gEkin7AKZPAy8uG+g7qMQVn3+FYDw28HBAraCrf0U /Y+G/7i2lR4UOLy0mGh4mBGpfOu34+sXZn4gVb4M= Received: from eusmges1new.samsung.com (unknown [203.254.199.242]) by eucas1p1.samsung.com (KnoxPortal) with ESMTP id 20190121134452eucas1p10d95e2d17fb4a7938b18a7332736e541~74QFcMdqR1790717907eucas1p14; Mon, 21 Jan 2019 13:44:52 +0000 (GMT) Received: from eucas1p1.samsung.com ( [182.198.249.206]) by eusmges1new.samsung.com (EUCPMTA) with SMTP id 4B.5B.04441.4DCC54C5; Mon, 21 Jan 2019 13:44:52 +0000 (GMT) Received: from eusmgms2.samsung.com (unknown [182.198.249.180]) by eucas1p2.samsung.com (KnoxPortal) with ESMTP id 20190121134452eucas1p2565c85c3e078a8cf60a7e0641c74df0e~74QEof-tK1268712687eucas1p2Y; Mon, 21 Jan 2019 13:44:52 +0000 (GMT) X-AuditID: cbfec7f2-5c9ff70000001159-df-5c45ccd44f16 Received: from eusync3.samsung.com ( [203.254.199.213]) by eusmgms2.samsung.com (EUCPMTA) with SMTP id 4D.8F.04128.3DCC54C5; Mon, 21 Jan 2019 13:44:51 +0000 (GMT) Received: from AMDC2765.digital.local ([106.116.147.25]) by eusync3.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTPA id <0PLO00H8FOUNWP10@eusync3.samsung.com>; Mon, 21 Jan 2019 13:44:51 +0000 (GMT) From: Marek Szyprowski To: linux-usb@vger.kernel.org Cc: Marek Szyprowski , Greg Kroah-Hartman , Minas Harutyunyan , Felipe Balbi , Bartlomiej Zolnierkiewicz , Andrzej Pietrasiewicz Subject: [PATCH v3] usb: dwc2: gadget: Add scatter-gather mode Date: Mon, 21 Jan 2019 14:44:47 +0100 Message-id: <20190121134447.13944-1-m.szyprowski@samsung.com> X-Mailer: git-send-email 2.17.1 In-reply-to: <20181214140741.21856-1-andrzej.p@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFprHIsWRmVeSWpSXmKPExsWy7djPc7pXzrjGGPy+pmaxYs0TRouNM9az Wsy/mWTRvHg9m0Xjr73sFouWtTJbrD1yl92B3WPnrLvsHvNOBnrsn7uG3aNvyypGjy37PzN6 fN4kF8AWxWWTkpqTWZZapG+XwJVxeEU/c8Ebp4otc8IbGD+adjFycEgImEhsXCXaxcjFISSw glHi5ZUbbBDOZ0aJDUs3M8IUbW1NhYgvY5TY3vibBcJpYJK49eApUAcnB5uAoUTX2y4wW0RA VuLwld/MIDazwGImiR0bnUFsYQE7if4nvxlBbBYBVYmTy1eD1fMK2Ers+HERzJYQkJdYveEA WC+ngLVE19bPzCDLJAR62CQW7vjGClHkIvH1zQwWCFtG4vLkbhaIomZGifYZs9ihOhglts7Z ATXWWuLw8YusECfxSUzaNp0Z4jdeiY42IQjTQ2JyozTEZ32MElPXnGKbwCixgJFhFaN4amlx bnpqsWFearlecWJucWleul5yfu4mRmCsnf53/NMOxq+Xkg4xCnAwKvHw/rjnEiPEmlhWXJl7 iFGCg1lJhPe1u2uMEG9KYmVValF+fFFpTmrxIUZpDhYlcd5qhgfRQgLpiSWp2ampBalFMFkm Dk6pBkazPNvduxzYFm/LZ/p5JcffmuXaOvaY+mWVdeavK6v0OuaGhvat+dhZEbHVM2XJZ3eV BU2Wr3trzCovVk/aVZZpLJifr/HepGxpsPpSu7c+gl+evc+9d/FS07RFET5b6l9ahLFq+2us 21z/wWKf1P7YA/MYNygIcbx1KN9xeLeuz6a6S0HOE5VYijMSDbWYi4oTAYzg44axAgAA X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFupkluLIzCtJLcpLzFFi42I5/e/4Vd3LZ1xjDFa9srFYseYJo8XGGetZ LebfTLJoXryezaLx1152i0XLWpkt1h65y+7A7rFz1l12j3knAz32z13D7tG3ZRWjx5b9nxk9 Pm+SC2CL4rJJSc3JLEst0rdL4Mo4vKKfueCNU8WWOeENjB9Nuxg5OCQETCS2tqZ2MXJxCAks YZT4PrGFEcJpYpI4fGMFcxcjJwebgKFE19suNhBbREBW4vCV38wgRcwCy5kkbs27xwSSEBaw k+h/8psRxGYRUJU4uXw1WAOvgK3Ejh8XwWwJAXmJ1RsOgA3lFLCW6Nr6GcwWErCSmHPvA+ME Rp4FjAyrGEVSS4tz03OLjfSKE3OLS/PS9ZLzczcxAgNo27GfW3Ywdr0LPsQowMGoxMP7455L jBBrYllxZe4hRgkOZiUR3tfurjFCvCmJlVWpRfnxRaU5qcWHGKU5WJTEec8bVEYJCaQnlqRm p6YWpBbBZJk4OKUaGCtZHSRudosc012iec5p3qObwlWc9+537du1nrPq/c8DuW9yGVPvzJp9 aKqqiMexq26Z00tjSnmMZyhJFJbL14du2W0XNn9bzXqxhMhpyxJuHT/25Im6c87Xjwa/JZ0K vZWtl/CbH5AxFNH7d2Xpf4lPSnm/nhf3rO1a06jYs8n8X9tF6dpl5kosxRmJhlrMRcWJAKN9 ox8cAgAA X-CMS-MailID: 20190121134452eucas1p2565c85c3e078a8cf60a7e0641c74df0e CMS-TYPE: 201P X-CMS-RootMailID: 20190121134452eucas1p2565c85c3e078a8cf60a7e0641c74df0e References: <20181214140741.21856-1-andrzej.p@samsung.com> Sender: linux-usb-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-usb@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Andrzej Pietrasiewicz This patch adds support for transferring requests, which are non-contiguous in physical memory, i.e. the data buffer is described by a scatter-list. This allows transferring large requests without relying on error-prone contiguous buffer allocations. This way of allocating requests is already implemented in functionfs and TCM USB functions and automatically used if UDC driver advertises scatter-gather suppport. Signed-off-by: Andrzej Pietrasiewicz [mszyprow: fixed null pointer issue, rewrote commit message] Signed-off-by: Marek Szyprowski --- Andrzej no longer works at Samsung, so I decided to continue this work. Here are some implementation details, which helps to understand the proposed changes. Non-isochronous transfers: The code for filling a single DDMA entry is extracted from dwc2_gadget_config_nonisoc_xfer_ddma() as dwc2_gadget_fill_nonisoc_xfer_ddma_one() function. The original function now either calls the extracted code (in case of physically contiguous request buffer) or iterates over scatterlist elements, for each of them calling the mentioned code. Isochronous transfers: If the usb request contains a scatterlist, the address from the first element is used as dma address at dwc2_gadget_fill_isoc_desc() invocations. Current code for isoc transfers does not allow more than 4096 bytes per transfer, so it is assumed there is only one element in the scatterlist. If there are more, a warning is issued. Please see the fragment under the comment: /* In DDMA mode for ISOC's don't queue request if length greater * than descriptor limits. */ in dwc2_hsotg_ep_queue() to see the limits for isoc transfers. --- Changelog: v2..v3: - fixed null pointer issue when processing ep0 zlp - renamed _dwc2_gadget_config_nonisoc_xfer_ddma() to dwc2_gadget_fill_nonisoc_xfer_ddma_one() - rewrote commit message to explain the reason for the proposed changes v1..v2: - using_desc_dma() called to check if DDMA available --- drivers/usb/dwc2/gadget.c | 113 ++++++++++++++++++++++++++------------ 1 file changed, 77 insertions(+), 36 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 357beb40b7a3..af419034e438 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -835,22 +835,13 @@ static u32 dwc2_gadget_get_desc_params(struct dwc2_hsotg_ep *hs_ep, u32 *mask) return desc_size; } -/* - * dwc2_gadget_config_nonisoc_xfer_ddma - prepare non ISOC DMA desc chain. - * @hs_ep: The endpoint - * @dma_buff: DMA address to use - * @len: Length of the transfer - * - * This function will iterate over descriptor chain and fill its entries - * with corresponding information based on transfer data. - */ -static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep, +static void dwc2_gadget_fill_nonisoc_xfer_ddma_one(struct dwc2_hsotg_ep *hs_ep, + struct dwc2_dma_desc **desc, dma_addr_t dma_buff, - unsigned int len) + unsigned int len, + bool true_last) { - struct dwc2_hsotg *hsotg = hs_ep->parent; int dir_in = hs_ep->dir_in; - struct dwc2_dma_desc *desc = hs_ep->desc_list; u32 mps = hs_ep->ep.maxpacket; u32 maxsize = 0; u32 offset = 0; @@ -865,39 +856,77 @@ static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep, hs_ep->desc_count = 1; for (i = 0; i < hs_ep->desc_count; ++i) { - desc->status = 0; - desc->status |= (DEV_DMA_BUFF_STS_HBUSY + (*desc)->status = 0; + (*desc)->status |= (DEV_DMA_BUFF_STS_HBUSY << DEV_DMA_BUFF_STS_SHIFT); if (len > maxsize) { if (!hs_ep->index && !dir_in) - desc->status |= (DEV_DMA_L | DEV_DMA_IOC); + (*desc)->status |= (DEV_DMA_L | DEV_DMA_IOC); - desc->status |= (maxsize << - DEV_DMA_NBYTES_SHIFT & mask); - desc->buf = dma_buff + offset; + (*desc)->status |= + maxsize << DEV_DMA_NBYTES_SHIFT & mask; + (*desc)->buf = dma_buff + offset; len -= maxsize; offset += maxsize; } else { - desc->status |= (DEV_DMA_L | DEV_DMA_IOC); + if (true_last) + (*desc)->status |= (DEV_DMA_L | DEV_DMA_IOC); if (dir_in) - desc->status |= (len % mps) ? DEV_DMA_SHORT : - ((hs_ep->send_zlp) ? DEV_DMA_SHORT : 0); - if (len > maxsize) - dev_err(hsotg->dev, "wrong len %d\n", len); + (*desc)->status |= (len % mps) ? DEV_DMA_SHORT : + ((hs_ep->send_zlp && true_last) ? + DEV_DMA_SHORT : 0); - desc->status |= + (*desc)->status |= len << DEV_DMA_NBYTES_SHIFT & mask; - desc->buf = dma_buff + offset; + (*desc)->buf = dma_buff + offset; } - desc->status &= ~DEV_DMA_BUFF_STS_MASK; - desc->status |= (DEV_DMA_BUFF_STS_HREADY + (*desc)->status &= ~DEV_DMA_BUFF_STS_MASK; + (*desc)->status |= (DEV_DMA_BUFF_STS_HREADY << DEV_DMA_BUFF_STS_SHIFT); - desc++; + (*desc)++; + } +} + +/* + * dwc2_gadget_config_nonisoc_xfer_ddma - prepare non ISOC DMA desc chain. + * @hs_ep: The endpoint + * @ureq: Request to transfer + * @offset: offset in bytes + * @len: Length of the transfer + * + * This function will iterate over descriptor chain and fill its entries + * with corresponding information based on transfer data. + */ +static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep, + struct usb_request *ureq, + unsigned int offset, + unsigned int len) +{ + struct dwc2_dma_desc *desc = hs_ep->desc_list; + struct scatterlist *sg; + int i; + u8 desc_count = 0; + + /* non-DMA sg buffer */ + if (!ureq->num_sgs) { + dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &desc, + ureq->dma + offset, len, true); + return; } + + /* DMA sg buffer */ + for_each_sg(ureq->sg, sg, ureq->num_sgs, i) { + dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &desc, + sg_dma_address(sg) + sg->offset, sg_dma_len(sg), + sg_is_last(sg)); + desc_count += hs_ep->desc_count; + } + + hs_ep->desc_count = desc_count; } /* @@ -1011,7 +1040,13 @@ static void dwc2_gadget_start_isoc_ddma(struct dwc2_hsotg_ep *hs_ep) hs_ep->next_desc = 0; list_for_each_entry_safe(hs_req, treq, &hs_ep->queue, queue) { - ret = dwc2_gadget_fill_isoc_desc(hs_ep, hs_req->req.dma, + dma_addr_t dma_addr = hs_req->req.dma; + + if (hs_req->req.num_sgs) { + WARN_ON(hs_req->req.num_sgs > 1); + dma_addr = sg_dma_address(hs_req->req.sg); + } + ret = dwc2_gadget_fill_isoc_desc(hs_ep, dma_addr, hs_req->req.length); if (ret) break; @@ -1167,7 +1202,7 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg, offset = ureq->actual; /* Fill DDMA chain entries */ - dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, ureq->dma + offset, + dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, ureq, offset, length); /* write descriptor chain address to control register */ @@ -1466,7 +1501,13 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, */ if (using_desc_dma(hs) && hs_ep->isochronous) { if (hs_ep->target_frame != TARGET_FRAME_INITIAL) { - dwc2_gadget_fill_isoc_desc(hs_ep, hs_req->req.dma, + dma_addr_t dma_addr = hs_req->req.dma; + + if (hs_req->req.num_sgs) { + WARN_ON(hs_req->req.num_sgs > 1); + dma_addr = sg_dma_address(hs_req->req.sg); + } + dwc2_gadget_fill_isoc_desc(hs_ep, dma_addr, hs_req->req.length); } return 0; @@ -2054,13 +2095,12 @@ static void dwc2_hsotg_program_zlp(struct dwc2_hsotg *hsotg, dev_dbg(hsotg->dev, "Receiving zero-length packet on ep%d\n", index); if (using_desc_dma(hsotg)) { - /* Not specific buffer needed for ep0 ZLP */ - dma_addr_t dma = hs_ep->desc_list_dma; - if (!index) dwc2_gadget_set_ep0_desc_chain(hsotg, hs_ep); - dwc2_gadget_config_nonisoc_xfer_ddma(hs_ep, dma, 0); + /* Not specific buffer needed for ep0 ZLP */ + dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &hs_ep->desc_list, + hs_ep->desc_list_dma, 0, true); } else { dwc2_writel(hsotg, DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | DXEPTSIZ_XFERSIZE(0), @@ -4471,6 +4511,7 @@ static int dwc2_hsotg_udc_start(struct usb_gadget *gadget, hsotg->enabled = 0; spin_unlock_irqrestore(&hsotg->lock, flags); + gadget->sg_supported = using_desc_dma(hsotg); dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name); return 0;