From patchwork Wed Apr 26 11:50:11 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yoshihiro Shimoda X-Patchwork-Id: 9701273 X-Patchwork-Delegate: geert@linux-m68k.org Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 1A929603F4 for ; Wed, 26 Apr 2017 11:52:54 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 0709C28468 for ; Wed, 26 Apr 2017 11:52:54 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id EE7A02858D; Wed, 26 Apr 2017 11:52:53 +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=-6.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,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 B670A28468 for ; Wed, 26 Apr 2017 11:52:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1949599AbdDZLwv (ORCPT ); Wed, 26 Apr 2017 07:52:51 -0400 Received: from relmlor3.renesas.com ([210.160.252.173]:39488 "EHLO relmlie2.idc.renesas.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1763466AbdDZLwj (ORCPT ); Wed, 26 Apr 2017 07:52:39 -0400 Received: from unknown (HELO relmlir3.idc.renesas.com) ([10.200.68.153]) by relmlie2.idc.renesas.com with ESMTP; 26 Apr 2017 20:52:36 +0900 Received: from relmlii1.idc.renesas.com (relmlii1.idc.renesas.com [10.200.68.65]) by relmlir3.idc.renesas.com (Postfix) with ESMTP id C52BE4A79E; Wed, 26 Apr 2017 20:52:36 +0900 (JST) X-IronPort-AV: E=Sophos;i="5.37,254,1488812400"; d="scan'208";a="240959738" Received: from mail-hk2apc01lp0215.outbound.protection.outlook.com (HELO APC01-HK2-obe.outbound.protection.outlook.com) ([65.55.88.215]) by relmlii1.idc.renesas.com with ESMTP/TLS/AES256-SHA256; 26 Apr 2017 20:52:36 +0900 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=renesasgroup.onmicrosoft.com; s=selector1-renesas-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version; bh=x1Z4qY321NSuhhfqqWXsHA+C8Z7M9MjimX3oA+vU8KA=; b=IWE0cyu+TL0hRMyP7Xa0qEwzrjGG7Bn9tg9iHpLDNDRdX6e4nd9BBeSOkgqcxQhDiPdyT6lbx9JxOZAYJFESfwQy+/fEi3spJSUjn63ergFn92cD/hmuP1Xje7voR40jEBCAoKp76w2pjH+MdBQP8GgbJ55+zV3jHnQNSJJTq8g= Authentication-Results: kernel.org; dkim=none (message not signed) header.d=none; kernel.org; dmarc=none action=none header.from=renesas.com; Received: from localhost.localdomain (211.11.155.144) by HK2PR06MB1683.apcprd06.prod.outlook.com (10.167.73.9) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.1061.12; Wed, 26 Apr 2017 11:52:34 +0000 From: Yoshihiro Shimoda To: CC: , , , Yoshihiro Shimoda Subject: [PATCH 5/5] usb: gadget: udc: renesas_usb3: add support for dedicated DMAC Date: Wed, 26 Apr 2017 20:50:11 +0900 Message-ID: <1493207411-24416-6-git-send-email-yoshihiro.shimoda.uh@renesas.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1493207411-24416-1-git-send-email-yoshihiro.shimoda.uh@renesas.com> References: <1493207411-24416-1-git-send-email-yoshihiro.shimoda.uh@renesas.com> MIME-Version: 1.0 X-Originating-IP: [211.11.155.144] X-ClientProxiedBy: OS2PR01CA0039.jpnprd01.prod.outlook.com (10.164.161.149) To HK2PR06MB1683.apcprd06.prod.outlook.com (10.167.73.9) X-MS-Office365-Filtering-Correlation-Id: af7907cf-6545-4a5d-081d-08d48c9aba96 X-MS-Office365-Filtering-HT: Tenant X-Microsoft-Antispam: UriScan:; BCL:0; PCL:0; RULEID:(22001)(48565401081)(201703131423075)(201703031133081); SRVR:HK2PR06MB1683; X-Microsoft-Exchange-Diagnostics: 1; HK2PR06MB1683; 3:Xbzfi1gBg8lVbvhHUDZTzLi3IAPk9LexEtnQYtl5vRBA1LegLMOqEpH5eeG9pZQ17cHYHJEmCMGX5gK5XGJIW77g20HLH2powqVIfx3PwzNU/L8YfsAIpOK67HNxmHQR7mHlR5jA6arszWWAfml0Ru6nXVID+byN5R1I3xOOd+5aMedOw6lKJRfBopWFf/sy65tw+YhQzl1WJQT/pvFii8IZA9SZEpF5/xcfzeLifAJvYaynyQX7wXbYL2bQHqb3fi2QRujpxV8GbTkb4ju0eCy04lUox+XB7KkRKxoA69uqyBF0Lgoq3ttbOjOYu3dfEPW3Wg3SOykCDZeOJKxFRRyI5l3oEPsEurrMP4khtGY=; 25:NDD7r/KShUyaIRSrZrwznfC+rv1niGrMB7pY8LZ3XYJe495LurQWlHXQ4TOepyn5jF9deBW4A0hXBvGolEfD/dt2V8ByGPzhGY8Jnb2R3s543MLaqkxjG7U634nl2GGEH8NzPvzFIU4E6rNJuptsvnsvbDkx3wSxZ0E2vDHK24txZS7h5j3LDLtrc2txCLeo4yz7AxU9tNDUgqFc/cMIPRfieslMTXq10awxIVK+TrZvn53l2huQXkVNU5tLBY6vhRXvYVYWdWuOV8AMUoW4w2Dg5LIUJquqJIOFc4dKMqcM50R1Y+6pI2RjvKEsnVZAmSnLXURDPfaiBKNNRQDYujzq7c7OtNDhtdLGuB9JNZjZI+sw5+bRlnIdKHwXbF/TX4Cg8GkTlnq/k/Xlf0JQaYSGw18TzOKorOCY5qy4R4XhJWMPjLercTnxkxEMZv6qEJd+9GymeZkVuO2IWJOeRg== X-Microsoft-Exchange-Diagnostics: 1; HK2PR06MB1683; 31:LxIHeg8u1x8qG8GarO9y/bA8tsjlg+YnRVdoqZSmN8dOxnCzjQBAw8sMtr25dth0ZA9Z1u/w12IEt4EtnxmqQsBGTK0Jr7YPikI/1eKVxHyGG95w6lrOcve66+53NIQBjPqvL2kyUW107v+AjySZf0Z8yRTGj5u8JvLBZvzvGtMiRS8Twm/kiK5c6Gvi9TkC2heT9thK73L+AscsrClA8xWZgoJVMZUAvgaRcp2VH+NKL/uBQRWOlRvfXLbHnJeC; 20:ZoKSNA7GU+iZ1rxAzLwfmxbLPcldRmHtCgoNoDm+sK7h91Wg2fAcwTYk95C97367Nc49zk6tbbvlnd2x7ALboWK8Al7gJFFLjOw7JWjfx4Qo4ftl0sJu98xcIibngcwN2M3O6BQ9HoS2/NxuyLF78PzdiS46gYEFd/sLcbe09Uy+fO6gP0IW9XchsRPEdnAD6NmWbBxhconthVyZAe2OToQZz9kT3i9DGgjSbkoNlcRRCo50gpU+5opr6yNwWSwoJ3TpGNVPEZHYp7pBTBwGynxhCVqDtn1VgKhSaDusCKsLNpTHVHD/Bf3YXTiobQmQNJXyxS45iRe8g40Se6TME0DuzKJGzl/PFXmC13FmCD3lKrxSLt5sQuZ67Zj5JbjIAhifhL5+OXnL7YoxPcNvAouaB9TWgKRSMZ7cxz306XB7Msdw/c8nY69UHUPcsOoWTnUwAxMAiHDbMNXhewUb0fji4F1hoXV0gSb0ibIIDUDz8FYBqrnlNiln+zlwE+TF X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(6040450)(601004)(2401047)(5005006)(8121501046)(3002001)(10201501046)(93006095)(6055026)(6041248)(20161123560025)(201703131423075)(201702281528075)(201703061421075)(201703061750153)(20161123558100)(20161123562025)(20161123564025)(20161123555025)(6072148); SRVR:HK2PR06MB1683; BCL:0; PCL:0; RULEID:; SRVR:HK2PR06MB1683; X-Microsoft-Exchange-Diagnostics: 1; HK2PR06MB1683; 4:4v8K1G45FEnNkek3ZBHNS3eMiQr3UC7KRk46MaOf+VHLkaMSHBV8rqGAM+mv41QvkFcMKQuN0idssAbPVUfUQ16Yn233IXhwt5qYNFrpCG2QDZv91lsPVClje3NMhfoFBJQ2PUk4rtwv19SsAw61z7kiukpgbtWeFTkqLUdeVsmsGY4D+VpqP4da0/cZt79sUoQuPZlWuY59Rlm2wGxK22QB0pQ2ZvBdk83pvPuAu/ad//B8FkpIXbrkxTMJgJXwcyUcE8AVFhSOKXjUVkM90TacE2bGGom7vX+uBJm+YoWhGP06Im9MGww0xux+eHLviVmGNBjWTZoqpRzIjLRZUNZDzbX4jAAbbalH850iQ2ALsDJzY3uTYPtJef+EX1U4QpzezB+ov4ykyPq5Ypgprh2pcstPfD86bFC4SekYW/B5MUjbSWCbnOiEttZ0vM+hbrYz7dLDpx3D5CkPVAC80rZVPtwifv7jZRjoUFJUdSybKPxsVUKN6k84G7GC9g+9SYXgFbsF0wm0J/ZF2I0LCgEfDhsd3ZqmJ/snrZWbXTJrDxdgFFHhNlQ/mJvqcBSNWojqF7qg45ecVVbWoR9/I6mKp+e13uCj5Kz9EYnFpmB7sJoNjfkSEGLUKSu3W07jwZxUrEr4DYmUB+p8J5TzKx0aAmXCRex/mbbh5t/w84cwFI/9PW7OxF6yckyzFHO3Fmhb0Bn6N0X7gcezrZ1uqrgr5ubKjDe8jojAjEcpybxXLXvH8ADLR89L57ss8dC45lmlhobLDH9gO0y7SfFcq/MQ7R6B1o1f6ADjJ+FFCZU= X-Forefront-PRVS: 0289B6431E X-Forefront-Antispam-Report: SFV:NSPM; SFS:(10019020)(4630300001)(6009001)(6069001)(39850400002)(39450400003)(39860400002)(39410400002)(39400400002)(39840400002)(51234002)(54906002)(6512007)(53936002)(189998001)(42186005)(2351001)(2906002)(38730400002)(50986999)(76176999)(33646002)(66066001)(6506006)(6486002)(6666003)(107886003)(2950100002)(6916009)(42882006)(110136004)(4326008)(47776003)(25786009)(36756003)(78352004)(5003940100001)(5660300001)(305945005)(7736002)(3846002)(6116002)(50466002)(8676002)(81166006)(50226002)(48376002); DIR:OUT; SFP:1102; SCL:1; SRVR:HK2PR06MB1683; H:localhost.localdomain; FPR:; SPF:None; MLV:ovrnspm; PTR:InfoNoRecords; LANG:en; X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; HK2PR06MB1683; 23:SovSD/UJnrRjEdc42YqJXOvHmkAIfRpSufUOypOe3?= =?us-ascii?Q?5HgxtNI3j0NaZtP1+UCAd7XfNeY91ZTh82VZcy5Kz5Fwd2EVOW1GybnSx3F8?= =?us-ascii?Q?kOdoGAo2P7JOvv7dVVqK2o0/r+NWlthiFdelr79xvAGVVERNMWoGw6BOSCn6?= =?us-ascii?Q?EXWjSlolpWMZ5OHFUU6yi8eJe94/PXr+hhit7LQ82FFS2A4kArCW6KHS/cUX?= =?us-ascii?Q?J2rgRM4gRvlEEuxzr/OnSuPSuLZ6HS6oOUCtUYtwrcVHv2fJAP2sBriZCXvA?= =?us-ascii?Q?yduKK5LEhLZtcj+4hjrBRR89uK+PwRZRvMGf9CKZnwJETYbZhs50VeFrfLG7?= =?us-ascii?Q?lIIWF1uOT+wsT6/O8GZm1S1KrYAVKJLAOJKuK4AvxGRBTgV574a2HuwICeCp?= =?us-ascii?Q?Q+cz/AYjVUFeQy9LvJy6PBjgDNPF2oGQi8nd9Za34GZNtH5RnHrkiDtDvEzP?= =?us-ascii?Q?uZDZaCE0fLdJdW2uadFv77LRL3VYa02z8EEhf+NN2jkJW2CterMxGL8EZhkM?= =?us-ascii?Q?BH/mqyioNvSs/tx0og3jXxMX71kOMjWj5PYjZu7hz6Sl1cDbWE4m6H3OW/Nz?= =?us-ascii?Q?0teuT0kfCqmnbszq1PVYUW4Mu/FAHC57moHvxqp4k5+b8eAUJBwpKVS7mR4l?= =?us-ascii?Q?sgc2xLU0OjeyIE8SP660bw9F97OCcLCU4460mM+VK5gEagcmrQlvHKdljPdn?= =?us-ascii?Q?u8iHwNMGm4Q34gh0i7ENPpaX4JujNY/2hwMxENlgdBhwPGmVH1l835XQzhxu?= =?us-ascii?Q?bU1WbmGFQKSG2T8YNF8hRhBzYhXWcKLXfRRFlAMdmbWetBPiBDdndfjfv5Ea?= =?us-ascii?Q?f4OBELEEWO7aY54lvJB8Wk5PGco57hEbCZOQLsI9Q/Nc19XNy77OFgRMqO8P?= =?us-ascii?Q?0NXVMlC0C6yBzs9TiZq34oYo9s6b+WxdQyiCOjq14s6/W2FPsy+OHn/ov3aM?= =?us-ascii?Q?RMzrVGeGGt+TH9knrjvDgL1UmTa9HT9y3oma8nPk4JhR8SLesszKGX6LwDPn?= =?us-ascii?Q?7/DOtCv++IP3vKaue+h88wmwNGPs/vcAM/d3JZHIvO2vzner+jSZtExUyJW3?= =?us-ascii?Q?B5SijboK2NdtR4qvv5oW98MLX0FX06TFPVh8IjyQdfChHkYFj+v1NThn4AVi?= =?us-ascii?Q?zFdXBrHJfJDBf6peswfccG1aNiE0R9gtOsG4nxiY1JZ3RP23AmsFA=3D=3D?= X-Microsoft-Exchange-Diagnostics: 1; HK2PR06MB1683; 6:c9GpYlf6RhLG7R3yTMyRNodcs4KiR3oc5Fbl8cXJ7NBKJ6V9jTCBbN24+TNk5BwirgcBuU+WLwiDZRcY0yGXW68V4RRb2V5mVcb9/mRE5HZ2ZQVff/DCATWKfzFEAlY/ApOhCffeIq8GpFNPWj+yQEncbp4ECzEyrSIfPhM4tins2SBcCtB5qbPHIwFmnYgyaBQ+u/9f7+FQvMSyZoK8EdNtZFdJ7MWzRCc7Kl+km9+D84BXJNuICcmBGXhicdokOl+2e5aI7rP3ql+7+F0QW5l1WadOzuA1D9qxx3ewNrdkPHBcOHBkqmkzRwBTlhxD6V9mshVb07fi8+FxxpC/814Ny7hKwQa82gZQFmbC4OfaNwpH+fcuSSFUz0aU0J1P723eAB6gCUWGcbLIZZ4fNZ1hm05okI00QXEnqPogk7SwYxztNfLZF+XKAQnXcuRQlHelP85lcoG04ih6cQI2IMwsPoSRZMv6pivJRl+mo6iiR27fcfpKR5epvIla/wVUxrYHCLmxWOqytpTSTh/Rn84bpdl6cGGKcmGmQkng88teYoGj95VzQ9cd6h1jDDIxJhxqrm2+zxL+TPJBYW6DTQ==; 5:u4TxAZZ6pa6Aq48OCCeo1dFHjB3IFDNz7Yv8DJaIjKjoe4B2MG86xOZ5DVAqPF1Aszhthllqe0U3wIHgF+EH27qbUdNJ/ijUkf3gVF2iR/JWsfD3mQajq6SEtIE451iZw79B7qF68V+wMSLMqYlbew==; 24:0uDtNCxuxRUxgyFIU3WZqUL1mvqz9iIh8WFJKJPHEZxI/pgcxS1d35AjEhxF+lIT+hpRcWIkCOQxz7L5HOq73VulKfVrS6Gc3PY2DwL2EL4= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1; HK2PR06MB1683; 7:/2qGTbLa3Abph2p1Mt28eu3Rn6LFcu0SBRh2krWltfgJ/OekkmU2pYRJ8k0bFAF8SSRqydP+qgT0BTI+PGbFTPppyOjygQ2NVG184d/ze5Ga8cBZEAlwRd2iD89RL4BmO65qHFNFxH/MeqqFAEwbh3n5YpRASUR1tukgmLYsIzOcWCy2P57KOApWHOqPIWqJluGR83pJrdePzStxjmop6flSAzbE7DkUw10UZIapF6Qr5vpYr8rOfc1d/cH8RLIw/CrVc1syTyfPeDac/ouAfllo4bA7W04Y7Tu8zEI06vfjHi7sYj6KuzqosVVHzMA+fnz753lDh5rg/jHAP/RorA==; 20:xXhnkLl7CWcdiiYF8ampC/abgNhX8EknZiLnKn2OaOq/ic8QweFGMh63jaIkWmL6/684kae084Gmb++TCA9ziKcs1kzLU7BVaEVSrvpDZInaC6scgv+dKa1P9dyarmLdlugKLPzlJQ/1njWqS30MeNd8qt4LUd6oCHEmEVr/SMk= X-OriginatorOrg: renesas.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 26 Apr 2017 11:52:34.3860 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-Transport-CrossTenantHeadersStamped: HK2PR06MB1683 Sender: linux-renesas-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-renesas-soc@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The USB3.0 peripheral controller on R-Car SoCs has a dedicated DMAC. The DMAC needs a "PRD table" in system memory and the DMAC can have four PRD tables. This patch adds support for the DMAC. Signed-off-by: Yoshihiro Shimoda --- drivers/usb/gadget/udc/renesas_usb3.c | 393 ++++++++++++++++++++++++++++++++++ 1 file changed, 393 insertions(+) diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index cd4c885..389051e 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -9,6 +9,7 @@ */ #include +#include #include #include #include @@ -27,6 +28,8 @@ #define USB3_AXI_INT_ENA 0x00c #define USB3_DMA_INT_STA 0x010 #define USB3_DMA_INT_ENA 0x014 +#define USB3_DMA_CH0_CON(n) (0x030 + ((n) - 1) * 0x10) /* n = 1 to 4 */ +#define USB3_DMA_CH0_PRD_ADR(n) (0x034 + ((n) - 1) * 0x10) /* n = 1 to 4 */ #define USB3_USB_COM_CON 0x200 #define USB3_USB20_CON 0x204 #define USB3_USB30_CON 0x208 @@ -64,6 +67,22 @@ /* AXI_INT_ENA and AXI_INT_STA */ #define AXI_INT_DMAINT BIT(31) #define AXI_INT_EPCINT BIT(30) +/* PRD's n = from 1 to 4 */ +#define AXI_INT_PRDEN_CLR_STA_SHIFT(n) (16 + (n) - 1) +#define AXI_INT_PRDERR_STA_SHIFT(n) (0 + (n) - 1) +#define AXI_INT_PRDEN_CLR_STA(n) (1 << AXI_INT_PRDEN_CLR_STA_SHIFT(n)) +#define AXI_INT_PRDERR_STA(n) (1 << AXI_INT_PRDERR_STA_SHIFT(n)) + +/* DMA_INT_ENA and DMA_INT_STA */ +#define DMA_INT(n) BIT(n) + +/* DMA_CH0_CONn */ +#define DMA_CON_PIPE_DIR BIT(15) /* 1: In Transfer */ +#define DMA_CON_PIPE_NO_SHIFT 8 +#define DMA_CON_PIPE_NO_MASK GENMASK(12, DMA_CON_PIPE_NO_SHIFT) +#define DMA_COM_PIPE_NO(n) (((n) << DMA_CON_PIPE_NO_SHIFT) & \ + DMA_CON_PIPE_NO_MASK) +#define DMA_CON_PRD_EN BIT(0) /* LCLKSEL */ #define LCLKSEL_LSEL BIT(18) @@ -231,8 +250,50 @@ #define USB3_EP0_BUF_SIZE 8 #define USB3_MAX_NUM_PIPES 30 #define USB3_WAIT_US 3 +#define USB3_DMA_NUM_SETTING_AREA 4 +/* + * To avoid double-meaning of "0" (xferred 65536 bytes or received zlp if + * buffer size is 65536), this driver uses the maximum size per a entry is + * 32768 bytes. + */ +#define USB3_DMA_MAX_XFER_SIZE 32768 +#define USB3_DMA_PRD_SIZE 4096 struct renesas_usb3; + +/* Physical Region Descriptor Table */ +struct renesas_usb3_prd { + u32 word1; +#define USB3_PRD1_E BIT(30) /* the end of chain */ +#define USB3_PRD1_U BIT(29) /* completion of transfer */ +#define USB3_PRD1_D BIT(28) /* Error occurred */ +#define USB3_PRD1_INT BIT(27) /* Interrupt occurred */ +#define USB3_PRD1_LST BIT(26) /* Last Packet */ +#define USB3_PRD1_B_INC BIT(24) +#define USB3_PRD1_MPS_8 0 +#define USB3_PRD1_MPS_16 BIT(21) +#define USB3_PRD1_MPS_32 BIT(22) +#define USB3_PRD1_MPS_64 (BIT(22) | BIT(21)) +#define USB3_PRD1_MPS_512 BIT(23) +#define USB3_PRD1_MPS_1024 (BIT(23) | BIT(21)) +#define USB3_PRD1_MPS_RESERVED (BIT(23) | BIT(22) | BIT(21)) +#define USB3_PRD1_SIZE_MASK GENMASK(15, 0) + + u32 bap; +}; +#define USB3_DMA_NUM_PRD_ENTRIES (USB3_DMA_PRD_SIZE / \ + sizeof(struct renesas_usb3_prd)) +#define USB3_DMA_MAX_XFER_SIZE_ALL_PRDS (USB3_DMA_PRD_SIZE / \ + sizeof(struct renesas_usb3_prd) * \ + USB3_DMA_MAX_XFER_SIZE) + +struct renesas_usb3_dma { + struct renesas_usb3_prd *prd; + dma_addr_t prd_dma; + int num; /* Setting area number (from 1 to 4) */ + bool used; +}; + struct renesas_usb3_request { struct usb_request req; struct list_head queue; @@ -242,6 +303,7 @@ struct renesas_usb3_request { struct renesas_usb3_ep { struct usb_ep ep; struct renesas_usb3 *usb3; + struct renesas_usb3_dma *dma; int num; char ep_name[USB3_EP_NAME_SIZE]; struct list_head queue; @@ -270,6 +332,8 @@ struct renesas_usb3 { struct renesas_usb3_ep *usb3_ep; int num_usb3_eps; + struct renesas_usb3_dma dma[USB3_DMA_NUM_SETTING_AREA]; + spinlock_t lock; int disabled_count; @@ -298,8 +362,18 @@ struct renesas_usb3 { (i) < (usb3)->num_usb3_eps; \ (i)++, usb3_ep = usb3_get_ep(usb3, (i))) +#define usb3_get_dma(usb3, i) (&(usb3)->dma[i]) +#define usb3_for_each_dma(usb3, dma, i) \ + for ((i) = 0, dma = usb3_get_dma((usb3), (i)); \ + (i) < USB3_DMA_NUM_SETTING_AREA; \ + (i)++, dma = usb3_get_dma((usb3), (i))) + static const char udc_name[] = "renesas_usb3"; +static bool use_dma = 1; +module_param(use_dma, bool, 0644); +MODULE_PARM_DESC(use_dma, "use dedicated DMAC"); + static void usb3_write(struct renesas_usb3 *usb3, u32 data, u32 offs) { iowrite32(data, usb3->reg + offs); @@ -1059,6 +1133,273 @@ static void usb3_start_pipe0(struct renesas_usb3_ep *usb3_ep, usb3_p0_xfer(usb3_ep, usb3_req); } +static void usb3_enable_dma_pipen(struct renesas_usb3 *usb3) +{ + usb3_set_bit(usb3, PN_CON_DATAIF_EN, USB3_PN_CON); +} + +static void usb3_disable_dma_pipen(struct renesas_usb3 *usb3) +{ + usb3_clear_bit(usb3, PN_CON_DATAIF_EN, USB3_PN_CON); +} + +static void usb3_enable_dma_irq(struct renesas_usb3 *usb3, int num) +{ + usb3_set_bit(usb3, DMA_INT(num), USB3_DMA_INT_ENA); +} + +static void usb3_disable_dma_irq(struct renesas_usb3 *usb3, int num) +{ + usb3_clear_bit(usb3, DMA_INT(num), USB3_DMA_INT_ENA); +} + +static u32 usb3_dma_mps_to_prd_word1(struct renesas_usb3_ep *usb3_ep) +{ + switch (usb3_ep->ep.maxpacket) { + case 8: + return USB3_PRD1_MPS_8; + case 16: + return USB3_PRD1_MPS_16; + case 32: + return USB3_PRD1_MPS_32; + case 64: + return USB3_PRD1_MPS_64; + case 512: + return USB3_PRD1_MPS_512; + case 1024: + return USB3_PRD1_MPS_1024; + default: + return USB3_PRD1_MPS_RESERVED; + } +} + +static bool usb3_dma_get_setting_area(struct renesas_usb3_ep *usb3_ep, + struct renesas_usb3_request *usb3_req) +{ + struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep); + struct renesas_usb3_dma *dma; + int i; + bool ret = false; + + if (usb3_req->req.length > USB3_DMA_MAX_XFER_SIZE_ALL_PRDS) { + dev_dbg(usb3_to_dev(usb3), "%s: the length is too big (%d)\n", + __func__, usb3_req->req.length); + return false; + } + + /* The driver doesn't handle zero-length packet via dmac */ + if (!usb3_req->req.length) + return false; + + if (usb3_dma_mps_to_prd_word1(usb3_ep) == USB3_PRD1_MPS_RESERVED) + return false; + + usb3_for_each_dma(usb3, dma, i) { + if (dma->used) + continue; + + if (usb_gadget_map_request(&usb3->gadget, &usb3_req->req, + usb3_ep->dir_in) < 0) + break; + + dma->used = true; + usb3_ep->dma = dma; + ret = true; + break; + } + + return ret; +} + +static void usb3_dma_put_setting_area(struct renesas_usb3_ep *usb3_ep, + struct renesas_usb3_request *usb3_req) +{ + struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep); + int i; + struct renesas_usb3_dma *dma; + + usb3_for_each_dma(usb3, dma, i) { + if (usb3_ep->dma == dma) { + usb_gadget_unmap_request(&usb3->gadget, &usb3_req->req, + usb3_ep->dir_in); + dma->used = false; + usb3_ep->dma = NULL; + break; + } + } +} + +static void usb3_dma_fill_prd(struct renesas_usb3_ep *usb3_ep, + struct renesas_usb3_request *usb3_req) +{ + struct renesas_usb3_prd *cur_prd = usb3_ep->dma->prd; + u32 remain = usb3_req->req.length; + u32 dma = usb3_req->req.dma; + u32 len; + int i = 0; + + do { + len = min_t(u32, remain, USB3_DMA_MAX_XFER_SIZE) & + USB3_PRD1_SIZE_MASK; + cur_prd->word1 = usb3_dma_mps_to_prd_word1(usb3_ep) | + USB3_PRD1_B_INC | len; + cur_prd->bap = dma; + remain -= len; + dma += len; + if (!remain || (i + 1) < USB3_DMA_NUM_PRD_ENTRIES) + break; + + cur_prd++; + i++; + } while (1); + + cur_prd->word1 |= USB3_PRD1_E | USB3_PRD1_INT; + if (usb3_ep->dir_in) + cur_prd->word1 |= USB3_PRD1_LST; +} + +static void usb3_dma_kick_prd(struct renesas_usb3_ep *usb3_ep) +{ + struct renesas_usb3_dma *dma = usb3_ep->dma; + struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep); + u32 dma_con = DMA_COM_PIPE_NO(usb3_ep->num) | DMA_CON_PRD_EN; + + if (usb3_ep->dir_in) + dma_con |= DMA_CON_PIPE_DIR; + + wmb(); /* prd entries should be in system memory here */ + + usb3_write(usb3, 1 << usb3_ep->num, USB3_DMA_INT_STA); + usb3_write(usb3, AXI_INT_PRDEN_CLR_STA(dma->num) | + AXI_INT_PRDERR_STA(dma->num), USB3_AXI_INT_STA); + + usb3_write(usb3, dma->prd_dma, USB3_DMA_CH0_PRD_ADR(dma->num)); + usb3_write(usb3, dma_con, USB3_DMA_CH0_CON(dma->num)); + usb3_enable_dma_irq(usb3, usb3_ep->num); +} + +static void usb3_dma_stop_prd(struct renesas_usb3_ep *usb3_ep) +{ + struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep); + struct renesas_usb3_dma *dma = usb3_ep->dma; + + usb3_disable_dma_irq(usb3, usb3_ep->num); + usb3_write(usb3, 0, USB3_DMA_CH0_CON(dma->num)); +} + +static int usb3_dma_update_status(struct renesas_usb3_ep *usb3_ep, + struct renesas_usb3_request *usb3_req) +{ + struct renesas_usb3_prd *cur_prd = usb3_ep->dma->prd; + struct usb_request *req = &usb3_req->req; + u32 remain, len; + int i = 0; + int status = 0; + + rmb(); /* The controller updated prd entries */ + + do { + if (cur_prd->word1 & USB3_PRD1_D) + status = -EIO; + if (cur_prd->word1 & USB3_PRD1_E) + len = req->length % USB3_DMA_MAX_XFER_SIZE; + else + len = USB3_DMA_MAX_XFER_SIZE; + remain = cur_prd->word1 & USB3_PRD1_SIZE_MASK; + req->actual += len - remain; + + if (cur_prd->word1 & USB3_PRD1_E || + (i + 1) < USB3_DMA_NUM_PRD_ENTRIES) + break; + + cur_prd++; + i++; + } while (1); + + return status; +} + +static bool usb3_dma_try_start(struct renesas_usb3_ep *usb3_ep, + struct renesas_usb3_request *usb3_req) +{ + struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep); + + if (!use_dma) + return false; + + if (usb3_dma_get_setting_area(usb3_ep, usb3_req)) { + usb3_pn_stop(usb3); + usb3_enable_dma_pipen(usb3); + usb3_dma_fill_prd(usb3_ep, usb3_req); + usb3_dma_kick_prd(usb3_ep); + usb3_pn_start(usb3); + return true; + } + + return false; +} + +static int usb3_dma_try_stop(struct renesas_usb3_ep *usb3_ep, + struct renesas_usb3_request *usb3_req) +{ + struct renesas_usb3 *usb3 = usb3_ep_to_usb3(usb3_ep); + unsigned long flags; + int status = 0; + + spin_lock_irqsave(&usb3->lock, flags); + if (!usb3_ep->dma) + goto out; + + if (!usb3_pn_change(usb3, usb3_ep->num)) + usb3_disable_dma_pipen(usb3); + usb3_dma_stop_prd(usb3_ep); + status = usb3_dma_update_status(usb3_ep, usb3_req); + usb3_dma_put_setting_area(usb3_ep, usb3_req); + +out: + spin_unlock_irqrestore(&usb3->lock, flags); + return status; +} + +static int renesas_usb3_dma_free_prd(struct renesas_usb3 *usb3, + struct device *dev) +{ + int i; + struct renesas_usb3_dma *dma; + + usb3_for_each_dma(usb3, dma, i) { + if (dma->prd) { + dma_free_coherent(dev, USB3_DMA_MAX_XFER_SIZE, + dma->prd, dma->prd_dma); + dma->prd = NULL; + } + } + + return 0; +} + +static int renesas_usb3_dma_alloc_prd(struct renesas_usb3 *usb3, + struct device *dev) +{ + int i; + struct renesas_usb3_dma *dma; + + if (!use_dma) + return 0; + + usb3_for_each_dma(usb3, dma, i) { + dma->prd = dma_alloc_coherent(dev, USB3_DMA_PRD_SIZE, + &dma->prd_dma, GFP_KERNEL); + if (!dma->prd) { + renesas_usb3_dma_free_prd(usb3, dev); + return -ENOMEM; + } + dma->num = i + 1; + } + + return 0; +} + static void usb3_start_pipen(struct renesas_usb3_ep *usb3_ep, struct renesas_usb3_request *usb3_req) { @@ -1078,6 +1419,10 @@ static void usb3_start_pipen(struct renesas_usb3_ep *usb3_ep, goto out; usb3_ep->started = true; + + if (usb3_dma_try_start(usb3_ep, usb3_req)) + goto out; + usb3_pn_start(usb3); if (usb3_ep->dir_in) { @@ -1549,6 +1894,7 @@ static void usb3_irq_epc_pipen(struct renesas_usb3 *usb3, int num) pn_int_sta &= usb3_read(usb3, USB3_PN_INT_ENA); usb3_write(usb3, pn_int_sta, USB3_PN_INT_STA); spin_unlock(&usb3->lock); + if (pn_int_sta & PN_INT_LSTTR) usb3_irq_epc_pipen_lsttr(usb3, num); if (pn_int_sta & PN_INT_BFRDY) @@ -1603,12 +1949,49 @@ static void usb3_irq_epc(struct renesas_usb3 *usb3) } } +static void usb3_irq_dma_int(struct renesas_usb3 *usb3, u32 dma_sta) +{ + struct renesas_usb3_ep *usb3_ep; + struct renesas_usb3_request *usb3_req; + int i, status; + + for (i = 0; i < usb3->num_usb3_eps; i++) { + if (!(dma_sta & DMA_INT(i))) + continue; + + usb3_ep = usb3_get_ep(usb3, i); + if (!(usb3_read(usb3, USB3_AXI_INT_STA) & + AXI_INT_PRDEN_CLR_STA(usb3_ep->dma->num))) + continue; + + usb3_req = usb3_get_request(usb3_ep); + status = usb3_dma_try_stop(usb3_ep, usb3_req); + usb3_request_done_pipen(usb3, usb3_ep, usb3_req, status); + } +} + +static void usb3_irq_dma(struct renesas_usb3 *usb3) +{ + u32 dma_sta = usb3_read(usb3, USB3_DMA_INT_STA); + + dma_sta &= usb3_read(usb3, USB3_DMA_INT_ENA); + if (dma_sta) { + usb3_write(usb3, dma_sta, USB3_DMA_INT_STA); + usb3_irq_dma_int(usb3, dma_sta); + } +} + static irqreturn_t renesas_usb3_irq(int irq, void *_usb3) { struct renesas_usb3 *usb3 = _usb3; irqreturn_t ret = IRQ_NONE; u32 axi_int_sta = usb3_read(usb3, USB3_AXI_INT_STA); + if (axi_int_sta & AXI_INT_DMAINT) { + usb3_irq_dma(usb3); + ret = IRQ_HANDLED; + } + if (axi_int_sta & AXI_INT_EPCINT) { usb3_irq_epc(usb3); ret = IRQ_HANDLED; @@ -1708,6 +2091,7 @@ static int renesas_usb3_ep_disable(struct usb_ep *_ep) usb3_req = usb3_get_request(usb3_ep); if (!usb3_req) break; + usb3_dma_try_stop(usb3_ep, usb3_req); usb3_request_done(usb3_ep, usb3_req, -ESHUTDOWN); } while (1); @@ -1755,6 +2139,7 @@ static int renesas_usb3_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) dev_dbg(usb3_to_dev(usb3), "ep_dequeue: ep%2d, %u\n", usb3_ep->num, _req->length); + usb3_dma_try_stop(usb3_ep, usb3_req); usb3_request_done_pipen(usb3, usb3_ep, usb3_req, -ECONNRESET); return 0; @@ -1917,6 +2302,7 @@ static int renesas_usb3_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_role); usb_del_gadget_udc(&usb3->gadget); + renesas_usb3_dma_free_prd(usb3, &pdev->dev); __renesas_usb3_ep_free_request(usb3->ep0_req); @@ -2111,6 +2497,10 @@ static int renesas_usb3_probe(struct platform_device *pdev) if (!usb3->ep0_req) return -ENOMEM; + ret = renesas_usb3_dma_alloc_prd(usb3, &pdev->dev); + if (ret < 0) + goto err_alloc_prd; + ret = usb_add_gadget_udc(&pdev->dev, &usb3->gadget); if (ret < 0) goto err_add_udc; @@ -2129,6 +2519,9 @@ static int renesas_usb3_probe(struct platform_device *pdev) usb_del_gadget_udc(&usb3->gadget); err_add_udc: + renesas_usb3_dma_free_prd(usb3, &pdev->dev); + +err_alloc_prd: __renesas_usb3_ep_free_request(usb3->ep0_req); return ret;