Received: by 10.192.165.148 with SMTP id m20csp4122569imm; Mon, 23 Apr 2018 19:47:03 -0700 (PDT) X-Google-Smtp-Source: AIpwx49WoHJj6rhckEwxq1yZDBI6kIZynPC9yJfA/JrxAJY3kxL7Iqgn0AObNT4E4WWMXptrrfRz X-Received: by 10.99.60.12 with SMTP id j12mr19426994pga.203.1524538023101; Mon, 23 Apr 2018 19:47:03 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1524538023; cv=none; d=google.com; s=arc-20160816; b=LaC0I2Vi4th1Afw3OjMFPQCVNbsVOLlO0mnQ2F+yNX6NQ/k7tZCVu/l2ByVNaRy5jT DTEh11aEcF2swVLyxChap71rDSqmreXJZOQBxqhtUKM27rbEcCKZgewJ5LjbbsgcHS2Q fmhj9O8S9QwRGzViomEskXPekmOAZsFlhAPjFtsOYyjXppJM5IY1q/UCoRdR16KIykhD vfLW4DbIylJ0+xSMkbS+RIcBaX+nzWNE7T+ShbhuqsfvGu8MSOxHs7QicjtWkECACVhy v9UwwynV1oGR7l6/Zp5G+tkbSuVVj+91qdLeytGNK+6YyEfvovj4v8S7vdwwqc1gSt7l f6sg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:arc-authentication-results; bh=scZ29NqBydRAn3FWkou5vaBBfbH7/pChvC0+AdLlwdw=; b=jRRO52qM5V+Y8q7NY5KLosmQi8NqoCLQQTVuWvJySDtRyVwsjxqnGFzkWfFo2k22DD Nc5J/S08kYt1xUzxchAzDDBhYxaHLe0LuNdXraiOeeaqXwMEEHjkrP+wed5uEpErSsIe pf3AVaglQTCj/I994q+XGZYlbLwFnlsMq9e63b2I6dDH9L4Wi7AcyIQzCQ7OTcvjvcB/ GZAZDCsk5IFsYd9cPMlP9yZ4/X4WXaGUFYuEJypKR/IwhCllGh30sIaT02XPTn/tmEQ3 rgHSYX3k/IvtQUzszjS8J89NSGhIAYJ/CptQNcH/e/rY1vcye//YZfXynuAs+RoMusbL occg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 73si12299672pfb.204.2018.04.23.19.46.48; Mon, 23 Apr 2018 19:47:03 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932834AbeDXCpT (ORCPT + 99 others); Mon, 23 Apr 2018 22:45:19 -0400 Received: from regular1.263xmail.com ([211.150.99.140]:59513 "EHLO regular1.263xmail.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932798AbeDXCpL (ORCPT ); Mon, 23 Apr 2018 22:45:11 -0400 Received: from william.wu?rock-chips.com (unknown [192.168.167.129]) by regular1.263xmail.com (Postfix) with ESMTP id 464494ACD; Tue, 24 Apr 2018 10:45:06 +0800 (CST) X-263anti-spam: KSV:0; X-MAIL-GRAY: 0 X-MAIL-DELIVERY: 1 X-KSVirus-check: 0 X-ABS-CHECKED: 4 Received: from localhost.localdomain (localhost [127.0.0.1]) by smtp.263.net (Postfix) with ESMTPA id 2476D331; Tue, 24 Apr 2018 10:45:00 +0800 (CST) X-RL-SENDER: william.wu@rock-chips.com X-FST-TO: hminas@synopsys.com X-SENDER-IP: 58.22.7.114 X-LOGIN-NAME: william.wu@rock-chips.com X-UNIQUE-TAG: X-ATTACHMENT-NUM: 0 X-SENDER: wulf@rock-chips.com X-DNS-TYPE: 0 Received: from unknown (unknown [58.22.7.114]) by smtp.263.net (Postfix) whith SMTP id 1831IIM37L; Tue, 24 Apr 2018 10:45:04 +0800 (CST) From: William Wu To: hminas@synopsys.com, felipe.balbi@linux.intel.com, gregkh@linuxfoundation.org Cc: heiko@sntech.de, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org, linux-rockchip@lists.infradead.org, frank.wang@rock-chips.com, huangtao@rock-chips.com, dianders@google.com, daniel.meng@rock-chips.com, John.Youn@synopsys.com, william.wu@rock-chips.com, wzz@rock-chips.com, zsq@rock-chips.com, Allen.Hsu@quantatw.com, StanTsui@AOPEN.com Subject: [PATCH 1/2] usb: dwc2: alloc dma aligned buffer for isoc split in Date: Tue, 24 Apr 2018 10:43:43 +0800 Message-Id: <1524537824-22010-2-git-send-email-william.wu@rock-chips.com> X-Mailer: git-send-email 2.0.0 In-Reply-To: <1524537824-22010-1-git-send-email-william.wu@rock-chips.com> References: <1524537824-22010-1-git-send-email-william.wu@rock-chips.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org The commit 3bc04e28a030 ("usb: dwc2: host: Get aligned DMA in a more supported way") rips out a lot of code to simply the allocation of aligned DMA. However, it also introduces a new issue when use isoc split in transfer. In my test case, I connect the dwc2 controller with an usb hs Hub (GL852G-12), and plug an usb fs audio device (Plantronics headset) into the downstream port of Hub. Then use the usb mic to record, we can find noise when playback. It's because that the usb Hub uses an MDATA for the first transaction and a DATA0 for the second transaction for the isoc split in transaction. An typical isoc split in transaction sequence like this: - SSPLIT IN transaction - CSPLIT IN transaction - MDATA packet - CSPLIT IN transaction - DATA0 packet The DMA address of MDATA (urb->dma) is always DWORD-aligned, but the DMA address of DATA0 (urb->dma + qtd->isoc_split_offset) may not be DWORD-aligned, it depends on the qtd->isoc_split_offset (the length of MDATA). In my test case, the length of MDATA is usually unaligned, this casue DATA0 packet transmission error. This patch base on the old way of aligned DMA allocation in the dwc2 driver to get aligned DMA for isoc split in. Signed-off-by: William Wu --- drivers/usb/dwc2/hcd.c | 63 +++++++++++++++++++++++++++++++++++++++++--- drivers/usb/dwc2/hcd.h | 10 +++++++ drivers/usb/dwc2/hcd_intr.c | 8 ++++++ drivers/usb/dwc2/hcd_queue.c | 8 +++++- 4 files changed, 85 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 190f959..8c2b35f 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -1562,11 +1562,20 @@ static void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg, } if (hsotg->params.host_dma) { - dwc2_writel((u32)chan->xfer_dma, - hsotg->regs + HCDMA(chan->hc_num)); + dma_addr_t dma_addr; + + if (chan->align_buf) { + if (dbg_hc(chan)) + dev_vdbg(hsotg->dev, "align_buf\n"); + dma_addr = chan->align_buf; + } else { + dma_addr = chan->xfer_dma; + } + dwc2_writel((u32)dma_addr, hsotg->regs + HCDMA(chan->hc_num)); + if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "Wrote %08lx to HCDMA(%d)\n", - (unsigned long)chan->xfer_dma, chan->hc_num); + (unsigned long)dma_addr, chan->hc_num); } /* Start the split */ @@ -2620,6 +2629,33 @@ static void dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg, } } +static int dwc2_alloc_split_dma_aligned_buf(struct dwc2_hsotg *hsotg, + struct dwc2_qh *qh, + struct dwc2_host_chan *chan) +{ + if (!qh->dw_align_buf) { + qh->dw_align_buf = kmalloc(chan->max_packet, + GFP_ATOMIC | GFP_DMA); + if (!qh->dw_align_buf) + return -ENOMEM; + + qh->dw_align_buf_size = chan->max_packet; + } + + qh->dw_align_buf_dma = dma_map_single(hsotg->dev, qh->dw_align_buf, + qh->dw_align_buf_size, + DMA_FROM_DEVICE); + + if (dma_mapping_error(hsotg->dev, qh->dw_align_buf_dma)) { + dev_err(hsotg->dev, "can't map align_buf\n"); + chan->align_buf = 0; + return -EINVAL; + } + + chan->align_buf = qh->dw_align_buf_dma; + return 0; +} + #define DWC2_USB_DMA_ALIGN 4 struct dma_aligned_buffer { @@ -2797,6 +2833,27 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) /* Set the transfer attributes */ dwc2_hc_init_xfer(hsotg, chan, qtd); + /* For non-dword aligned buffers */ + if (hsotg->params.host_dma > 0 && qh->do_split && + chan->ep_is_in && (chan->xfer_dma & 0x3)) { + dev_vdbg(hsotg->dev, "Non-aligned buffer\n"); + if (dwc2_alloc_split_dma_aligned_buf(hsotg, qh, chan)) { + dev_err(hsotg->dev, + "%s: Failed to allocate memory to handle non-dword aligned buffer\n", + __func__); + /* Add channel back to free list */ + chan->align_buf = 0; + chan->multi_count = 0; + list_add_tail(&chan->hc_list_entry, + &hsotg->free_hc_list); + qtd->in_process = 0; + qh->channel = NULL; + return -ENOMEM; + } + } else { + chan->align_buf = 0; + } + if (chan->ep_type == USB_ENDPOINT_XFER_INT || chan->ep_type == USB_ENDPOINT_XFER_ISOC) /* diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index 96a9da5..adf1fd0 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -76,6 +76,8 @@ struct dwc2_qh; * (micro)frame * @xfer_buf: Pointer to current transfer buffer position * @xfer_dma: DMA address of xfer_buf + * @align_buf: In Buffer DMA mode this will be used if xfer_buf is not + * DWORD aligned * @xfer_len: Total number of bytes to transfer * @xfer_count: Number of bytes transferred so far * @start_pkt_count: Packet count at start of transfer @@ -133,6 +135,7 @@ struct dwc2_host_chan { u8 *xfer_buf; dma_addr_t xfer_dma; + dma_addr_t align_buf; u32 xfer_len; u32 xfer_count; u16 start_pkt_count; @@ -303,6 +306,10 @@ struct dwc2_hs_transfer_time { * is tightly packed. * @ls_duration_us: Duration on the low speed bus schedule. * @ntd: Actual number of transfer descriptors in a list + * @dw_align_buf: Used instead of original buffer if its physical address + * is not dword-aligned + * @dw_align_buf_size: Size of dw_align_buf + * @dw_align_buf_dma: DMA address for dw_align_buf * @qtd_list: List of QTDs for this QH * @channel: Host channel currently processing transfers for this QH * @qh_list_entry: Entry for QH in either the periodic or non-periodic @@ -350,6 +357,9 @@ struct dwc2_qh { struct dwc2_hs_transfer_time hs_transfers[DWC2_HS_SCHEDULE_UFRAMES]; u32 ls_start_schedule_slice; u16 ntd; + u8 *dw_align_buf; + int dw_align_buf_size; + dma_addr_t dw_align_buf_dma; struct list_head qtd_list; struct dwc2_host_chan *channel; struct list_head qh_list_entry; diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index a5dfd9d..5e2378f 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -938,6 +938,14 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg, frame_desc->actual_length += len; + if (chan->align_buf) { + dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, DMA_FROM_DEVICE); + memcpy(qtd->urb->buf + frame_desc->offset + + qtd->isoc_split_offset, chan->qh->dw_align_buf, len); + } + qtd->isoc_split_offset += len; hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index e34ad5e..a120bb0 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -1693,8 +1693,14 @@ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) dwc2_host_put_tt_info(hsotg, qh->dwc_tt); - if (qh->desc_list) + if (qh->desc_list) { dwc2_hcd_qh_free_ddma(hsotg, qh); + } else { + /* kfree(NULL) is safe */ + kfree(qh->dw_align_buf); + qh->dw_align_buf_dma = (dma_addr_t)0; + } + kfree(qh); } -- 2.0.0