From patchwork Wed Jan 4 13:23:00 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Manish Narani X-Patchwork-Id: 9497855 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 870AF606A9 for ; Wed, 4 Jan 2017 21:33:32 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 7B19C282E2 for ; Wed, 4 Jan 2017 21:33:32 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6F16528339; Wed, 4 Jan 2017 21:33:32 +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=-4.1 required=2.0 tests=BAD_ENC_HEADER,BAYES_00, DKIM_SIGNED,RCVD_IN_DNSWL_MED,T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 0C49D282E2 for ; Wed, 4 Jan 2017 21:33:29 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.87 #1 (Red Hat Linux)) id 1cOt98-0006I1-Nx; Wed, 04 Jan 2017 21:30:58 +0000 Received: from casper.infradead.org ([2001:770:15f::2]) by bombadil.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1cOsqJ-00062J-47 for linux-arm-kernel@bombadil.infradead.org; Wed, 04 Jan 2017 21:11:31 +0000 Received: from mail-bn3nam01on0060.outbound.protection.outlook.com ([104.47.33.60] helo=NAM01-BN3-obe.outbound.protection.outlook.com) by casper.infradead.org with esmtps (Exim 4.87 #1 (Red Hat Linux)) id 1cOlXa-0007ax-VN for linux-arm-kernel@lists.infradead.org; Wed, 04 Jan 2017 13:23:52 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=xilinx.onmicrosoft.com; s=selector1-xilinx-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version; bh=k2kmAOgOex5yznBjdxYAWXBZWGWD0VPlTOoPBOpsCr4=; b=yeC3uw3e8T2TSRSMGO49B1jwwwoMLyPe/HmPn9Ge/WuY4TayosjpCPcUVEmC91Ufzn9s1JlR14v5Q8/w0waX5+iQf5XtB3b8BpFcljGAH7enPx3DbFRRb0H+t1bif2F0NO5cWdvVv3HClkfZ7SY64t1LsvmGD5vFvvfXKp9fmUU= Received: from BN6PR02CA0058.namprd02.prod.outlook.com (10.175.94.148) by BY1PR0201MB1078.namprd02.prod.outlook.com (10.161.205.140) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P384) id 15.1.803.11; Wed, 4 Jan 2017 13:23:18 +0000 Received: from CY1NAM02FT055.eop-nam02.prod.protection.outlook.com (2a01:111:f400:7e45::206) by BN6PR02CA0058.outlook.office365.com (2603:10b6:404:f9::20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384_P384) id 15.1.817.10 via Frontend Transport; Wed, 4 Jan 2017 13:23:18 +0000 Authentication-Results: spf=pass (sender IP is 149.199.60.83) smtp.mailfrom=xilinx.com; apm.com; dkim=none (message not signed) header.d=none;apm.com; dmarc=bestguesspass action=none header.from=xilinx.com; Received-SPF: Pass (protection.outlook.com: domain of xilinx.com designates 149.199.60.83 as permitted sender) receiver=protection.outlook.com; client-ip=149.199.60.83; helo=xsj-pvapsmtpgw01; Received: from xsj-pvapsmtpgw01 (149.199.60.83) by CY1NAM02FT055.mail.protection.outlook.com (10.152.74.80) with Microsoft SMTP Server (version=TLS1_0, cipher=TLS_RSA_WITH_AES_256_CBC_SHA) id 15.1.803.8 via Frontend Transport; Wed, 4 Jan 2017 13:23:16 +0000 Received: from unknown-38-66.xilinx.com ([149.199.38.66] helo=xsj-pvapsmtp01) by xsj-pvapsmtpgw01 with esmtp (Exim 4.63) (envelope-from ) id 1cOlXA-00071k-3a; Wed, 04 Jan 2017 05:23:16 -0800 Received: from [127.0.0.1] (helo=localhost) by xsj-pvapsmtp01 with smtp (Exim 4.63) (envelope-from ) id 1cOlXA-0007f7-0Y; Wed, 04 Jan 2017 05:23:16 -0800 Received: from [172.23.64.139] (helo=xhd-lin64re116.xilinx.com) by xsj-pvapsmtp01 with esmtp (Exim 4.63) (envelope-from ) id 1cOlX4-0007ct-Sa; Wed, 04 Jan 2017 05:23:11 -0800 Received: by xhd-lin64re116.xilinx.com (Postfix, from userid 16987) id 861BE302685; Wed, 4 Jan 2017 18:53:10 +0530 (IST) From: Manish Narani To: , , , , , , , , , , , , , , , , , , Subject: [RFC PATCH] usb: dwc3: otg: Adding OTG driver for DWC3 controller Date: Wed, 4 Jan 2017 18:53:00 +0530 Message-ID: <1483536181-22356-6-git-send-email-mnarani@xilinx.com> X-Mailer: git-send-email 2.1.1 In-Reply-To: <1483536181-22356-1-git-send-email-mnarani@xilinx.com> References: <1483536181-22356-1-git-send-email-mnarani@xilinx.com> X-TM-AS-Product-Ver: IMSS-7.1.0.1224-8.0.0.1202-22802.006 X-TM-AS-User-Approved-Sender: Yes;Yes X-EOPAttributedMessage: 0 X-MS-Office365-Filtering-HT: Tenant X-Forefront-Antispam-Report: CIP:149.199.60.83; IPV:NLI; CTRY:US; EFV:NLI; SFV:NSPM; SFS:(10009020)(6009001)(7916002)(39410400002)(39840400002)(39850400002)(39860400002)(39450400003)(2980300002)(438002)(199003)(189002)(54906002)(50986999)(107886002)(90966002)(45336002)(52956003)(36756003)(16601075003)(2906002)(76176999)(626004)(103686003)(4326007)(5660300001)(6666003)(33646002)(50466002)(305945005)(356003)(50226002)(47776003)(189998001)(48376002)(92566002)(38730400001)(36386004)(8936002)(2201001)(2950100002)(575784001)(42186005)(7416002)(81166006)(81156014)(5001770100001)(8676002)(4001430100002)(5003940100001)(46386002)(106466001)(63266004)(921003)(107986001)(2004002)(2101003)(83996005)(1121003)(579004)(559001); DIR:OUT; SFP:1101; SCL:1; SRVR:BY1PR0201MB1078; H:xsj-pvapsmtpgw01; FPR:; SPF:Pass; PTR:unknown-60-83.xilinx.com; A:1; MX:1; LANG:en; X-Microsoft-Exchange-Diagnostics: 1; CY1NAM02FT055; 1:KLHzweTcrg5SiAUBphgyYhZC95DFal5aWniJ2KJf3gcuNDPJznioLLKgV2lyAUsyfWjRFcF1yqH7zxQr9xI9oqlUe+Cehu0L97lSfL/DrpNQOntXOBcJJlMniDHyKUSBBsp5YlzhOVtrmc2mSx4WaDs9341uiO87nI3sfQwIocg9VSDyjSgu4RT5IOnBU3pUOvUz/PiIrzR9P1QjTzV8BhXPebwmCNSsC6qa/ssEHkDOHx9883xqvTbGzdUdEUzfWY5JowUxZ3x1DXR47zfZw+hXqKb5JwU8+0kFs5Upk4NgULsB3sD+dhMeyt4KeP1urlQFAYpxHoh9D3GFEtS4Pois9uIviZ0dAD3PjpZhVlGjFbJ5i/KDNKviQ65kMf+m6Q8F1B7ZN8E6gSbpzHnWnoAVJ5ot/uUsGgQgszNxKJeIyn8uNDnwBE/HEF2FO8aK2ooO2jrtdT3ujgRnw0cn5Dcjw6Urpi+oQA0GBdgEoo2OmL4Tn6SLGW40AxF2odVnYz74iWDO88k9f606Jmplr32HLt4Ueplmh+s1PCG0JjYn/+IGZRFwm/+LqjVPtTfcsBxh1EJ+YlelC0mi0U69Ur/sdRvxSGO3Mw1hJaPFMxLqncJ9r4pnO+0j1idKwhdiXsEW32Mf8PpvND6COGdOwg== MIME-Version: 1.0 X-MS-Office365-Filtering-Correlation-Id: 83418331-851b-4242-d1d7-08d434a4d85a X-Microsoft-Antispam: UriScan:; BCL:0; PCL:0; RULEID:(22001)(8251501002); SRVR:BY1PR0201MB1078; X-Microsoft-Exchange-Diagnostics: 1; BY1PR0201MB1078; 3:aftQAMPlpBmO2ppPUo99cwLH1/nlC18hac4L0VrmhIsq5jL6tOvg06geelcqpRvY6V7HHX3qyEBaqX/1S+RXtZqhJH6OHcabwZtOiCIydCRTYABvKaU+uH6rY8D4irbY9dJgthNt7rk0jqXdfzUTqg/M1HWVOp3MALWxJ6Ypma/j/EGSqql8fJzkXfXWHtEYJRNKAKr8g/Guily7A+lKW+YiWDH/mYLYgn9zbGLQ0c2pChsSmpIOVSbE3k/pEUTndzycoq99t4ng8l24VFAWajx4q7JYnbyYwTocaAnQbAAsVZY/UPKbo6+C/tyf9ckZhQgFsNSaEAwVs2SRzJ2w4mLTyT0hVdpPMyVS23nKv/fTESR720zvg6Z5km5um+dPz3LaiWSv33pnsYG1AbbD6A== X-Microsoft-Exchange-Diagnostics: 1; BY1PR0201MB1078; 25:wy+Vb9jl/9DWKn2Kk1h+XxGnkCegWFhpVQ7GBXPw42MdzdNECysqnYm9YIU0pjITFrxgT/Jcjpug6Wkeb9xMTGTZ3F2TEUpUPa1YdxTcqcjm8FNfiXAqI1lWf6AZsOP6c/faw8DPb42P6S5KwDUqIk7Z1Z51dlZgU9/WazhYZth7LZmG8NQJKLq2oEcm63/pS3OBWsQg7bG8UioCKz3XnaSSFU6gkA9UCRkQh3NQ5pG+ZH2F5eXhnPmrlqyEVWOVOZ+vwqdnt+MSUSdfUHtzX/Yr1WkNwtl5YRK/B8T/ysP83pzH2RvsbmIpZb2Z7zOK1WCSWFrlFu7X04O45/tOMo0qlb5Km0q31iLst393wNkfj82kyDfzxyZIuPYkprLlW1pnoOC0IB/HwCuGSv2CY5G96sATQXsS7PFR1ls5//PgsQtdsbn9VkJ7a3iiwO9MLlYGlKN40dqp1Ds3IEoCGWrGlAHM97d9F9nGkSD8cgc9KBjOEx0gjCW8jqMiQw6aWhmo5IzlodD9PM2doauG/SzMw6JihkveGhhmmjl7zjuDh8nZBxKuiOd3dlSxnVLQHU4SRtUjQfBUIIN0MMy+KPv3IbPNVnEFtCmf6LiTIYY/GUdrK0lIGa4Ws1fzXgFOi0rH3vMjSU38YzCK+x0SzmP6oxlLCdIfJWxpunIcI1tKuZ1RmXxGHm4EgKtwbvPfycANfgo7tXy/bxLRsz62PWk+irfAhE7hO7CtaeByPDtCJFTN7/jAGcmxocIuODPMHtydoQGf73FBo0Md3WCPEpAQ2n9/g09OH/gmV6og/qBx7FGGl2WvhoWS5pjVm7mI6coXwNQu0HDlS+uUZ5HvEc6xUTBoVqJyGrVPsZnywu23M0Is2GFOoLoEpuvAjRgxa+kbMobDyirr3/qg4W8lxQ== X-Microsoft-Exchange-Diagnostics: 1; BY1PR0201MB1078; 31:9b0yJ1ckUDa2LCIXYz9rIHt9EYR0JpuBuv13aaMVJ+sffXMPhOA8p0ZAXA3HoKC+mR2Drxt/ZyLONKAfB8xzfngYNLg1Fy6c7Cno2EY1sw6pWjETNUJZ6gvp1GlvrNe1V5FMoWYFlP645muhpgEllJr378PvtA4I4Prwzk+mo/BBMjnfSUSb3KaKKC/I609agcnx5rjMwUP3WE5Yo4qIuiu56idiKB3w/S24UntmOM/wsZUT8RQLD4/xoAG85aiyTkxOv6k0bByx9OMVHytwPy+3STUIDPrHekb7lHlj477VZcrYa27cJeFAph2hGfYK; 20:J8uOrmfz8Keb9HM/XWbvm2cTxRm1dvlrNh/QOYvWtRdcjaJ7Ci2K65E7TQA/i9Gqr1mS9m3IJ9HnG8RiTdYHelguj3x5FAwXv27xTHS3PQ9D0NUjoavAuO/kcPtgWLwyyBAjETT8u/7e4sxUA0ceOQmoQjWRRrc8DXtOoyYYrPrrTWq0+wD5E52yk4VUjVH6qwCP4+h20a9enORMadx6ONk77jNZUvv4TVID77l79NzO8e/Om6N3D2LK88Amw4flY5Tf072J/DaIxApMg1Yc0AIEVhqR+fhY8qx9xrc7SGHLhlHgvp4nWen96HIl50kbNJK7pP5O15ufeGAN4bJXQ67e0SL/FIHYF5db6NJw5wdBgqZGJj+lYfvKaymVEbo6wYKY+w+Fw/NmEBib4b6FxqERqAX1ThCuxKppQlsd9MVdqUmmJz21jpMVSsmLYPXChdsi/cq5qh3WtHskbcLCD79vjJN48DIXqtdZNwY2SPk+XVw78ZSAUy22pX1mtjcw X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:(31051911155226)(192813158149592)(106291317490208)(21532816269658); X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(6040375)(601004)(2401047)(13023025)(13024025)(13015025)(13017025)(13018025)(8121501046)(5005006)(10201501046)(3002001)(6055026)(6041248)(20161123555025)(20161123560025)(20161123558021)(20161123564025)(20161123562025)(6072148); SRVR:BY1PR0201MB1078; BCL:0; PCL:0; RULEID:; SRVR:BY1PR0201MB1078; X-Microsoft-Exchange-Diagnostics: 1; BY1PR0201MB1078; 4:t1UJgDWdqHd85rNpk/1iyfuoE9UaEddvy0uZ64Cn+BCWvAI7ppbCTEufsMvJDRLWLr0Gw3wOhQ/MO/vq/hcx5dgWEx4wODjRTGq2d8ilGVo8ALUcfZB7JOA3f2/k3lEYJ3IRgcf56AyPL20ciBDvKuHPMhQKHI7U7zZv96OMV/Gi3dvMON+DmUs45IMlz9tl2/TdMaUnXW+YgYF/+Aq73nbbHOcOvvcDw8YgxXmXI7I1LwSvxlC9txWwR2+6wWxCXZepFQKbR/UQ/zCzifox3mtbi1/yHuwrk/MXuN2H8MBXZmE2i+HSU7QYutOxgHVppiO/K7j2SE8QhZqiUog+OboMk3ORQf+Gv4T9+FtuXeryVB54eCZm1ac3eT/XojzOofvaB1s3+HKEgqo/RNhfpnaPecBIbei5z/YWc2IjlPk/l8aBLmghJda+rsyxCWADM8EECCmjksZOtj4S5H64iTyRdjAH73jfvX3LU9ieD/ukONH5MN6/GnmvDDxAhaqjnTVMLGpQxm7M3UWO6MPRZjthGeQTjpmkjQB746EPA4xC1RbLEV4yJeiLdMu19Xq+ouv8GbwKUocfpJ/SslAmW0KurmqnuDXXtM+sJ25L8kvJFXZ1P3u/2R+5IQvS0WWb6yIRcN0cSA/rWaDKLVVEoAFdQCy+aqIghOX2HpYZfyWC3C+gSP2OsTYfTZKBqz9WuupXyPVUszQgNAJWqJSteyfULCQ9ImbhBPMdhWTOdBDlCgGWUHBG/nY7OM6GsNU2NfU6lUXOibSSMReqkf/z3X9XVaLATXwDOqmk/cdGYmjIHoYyMle5XdF2EkPfrg/ft5dXl8hFJUjrZoqxZG42fP1i/RYjpP9neyl09nYgkolYCBwvqXNGi+FOMopJtPMR X-Forefront-PRVS: 0177904E6B X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1; BY1PR0201MB1078; 23:rvEYiaYYh4Qw58EThnHE8ZVzgzmvbVyl9qIKSr7?= =?us-ascii?Q?edjKcRjKSLfuU/2rbnkrqrJhFLlXvhPlQycrSQAOjXM7KkwiEDJ+1egZaDJX?= =?us-ascii?Q?68sZ1piSQlTAmddrnjKTazrjA7o5Inv6uKhpy9h4d5Uj7qVVPVXHF0jVx4+O?= =?us-ascii?Q?Vqg1j8+zEYtNYbqFBIuKe/IsRTw5l95JwExLuKv5KrusABE4+wkyfRGsOSAq?= =?us-ascii?Q?2TMoe3UHhiebq2UkcsGBWg4If9ypOzOgQeBFqhhJJ2R2EZcMl4LrQoi95OPi?= =?us-ascii?Q?P/ZdcERLnVa7HmeY2QY8BmhdPBW2YRRohPQIrIjeWPmPcuOCuRB+SIPO4LuJ?= =?us-ascii?Q?r1G5D+dzridWLIOUJJiKUNksS1KVZ+npY90zX4hMVhjryDpsZ9+3+NlqZMSE?= =?us-ascii?Q?ZMWyij7KAtI6ILuDVMqsxebWlHy+ozEV4B+egmpOAw0W71TLGzTSqLKc/Zqt?= =?us-ascii?Q?mywWo8Pv+jkbk/THBFuxij3tq9sPGJD57sOSBnjtCKHY2640yw9TUjYWl6Ju?= =?us-ascii?Q?2bAh+XRco6HXoq/hqGnTIqMGoeS6JbL3RU/Si6UHh82QVQnPCNfNS46+lvUN?= =?us-ascii?Q?VKe7q9y1E2rwQjhHhqQz2pZP3pdUftwSRIaTeKyxAdCyr+rLR6VFhk/sj937?= =?us-ascii?Q?sM1TuKS/2IFzTPOx1s2tW4k7JcdgztyVoP5q9Uxm0l89Q3hjcLdNCIWo0w90?= =?us-ascii?Q?oGkwlfMt89sNOvh/P3XcA9Xr8GTei45dtzDv2GMdRSS9CWG1pdwRRaRIsiuz?= =?us-ascii?Q?6gXWI36VE2az/IwKHW0KoSV3mxdnT89hIZtznjtUcD20GVj+7swUw0od6nX/?= =?us-ascii?Q?C+RFdqVYH6yxcqM99I5V2yuuOvqpcJ1wEi905v/6EbSEMQBVt3du0DIaOTT3?= =?us-ascii?Q?MtusC5B9h4NyyaWphJv77Q3dpovngkZ4kTLCOvR1YIkotNMm7Xf0oCmunOKp?= =?us-ascii?Q?wretz/HxwddslO8m+djUq6nr7pQ9vsTqM2t+dSmHXYqK8y266qIBlS+h/nAH?= =?us-ascii?Q?56uZJoHcASE9pnt1nBBpeZ2wf0+1nj6upnAK2Zg0NHnhsA+1M+DKDS2CMCcK?= =?us-ascii?Q?a6NQ537We9iTIW+LJ9RlqhX+4+vVI5ZOkwL5wqPPfIettFWefNSq/qWvaDkr?= =?us-ascii?Q?HH5OHGLfvXjVdZufv3pl+gl8D7UYs0/cFluGb1DGXiHhBPbHavs8Rhq9iB34?= =?us-ascii?Q?74pFGQEdINVAWweiI7THzyqeSi/I5cVDk/YPuuzJ+Ku2VP0NtP2r/8MiZ/fs?= =?us-ascii?Q?i1X6/Srys2f8nPpBhu+AFFL19OtEHMnG9qWwKd0a9Vbl4AR2ntTXvud+K13i?= =?us-ascii?Q?up/Yvdm0keN63qGx2rLF3XueyN58PVzrRcuTLOC2kZtxLZM4zTMTLuM+BI46?= =?us-ascii?Q?O5YYfGYLexQfaDW6zodHUI9D7DAJQpYSj/cDiVn50AF9gN0tL06A+DO+KNCT?= =?us-ascii?Q?YrkQEFnyFXNTP+oDp93HfFm08gnJWSoM=3D?= X-Microsoft-Exchange-Diagnostics: 1; BY1PR0201MB1078; 6:VdwQk3AO97FmxwFsqPbQ+gjXPQyM9fyV5cRvWJ6/Yfuu9JUUGT1p5bijSNwU6UpP4hfwvDufMq/lZ01b60OONCypoCWUFjhRz344imIIlNQ2C/rY+L8Ad2xKJLI1OH5FrCpcfkYMjO8v7ut/m66Mp6IVSJvPA3MMcjgZ679K0aEcZSIBb5j5UwQv6b+ptt91nv9UdskXsz9IttYgvU9etKXExe/UIct1sdP1jm3rkTIQN+/e1H9pwRmc9BXOg065qsc9q5H4weSue0lTn7nLJn2W5IVgeTT2l2gcnib8aJ1Hsp31+UeM1DbjGriCBzquPuCGmqwTfUOrxEMFQM4b3PTGLU0lA5btuTq2/4JLcnqFCpfOmqm+5YqSeyh0mgC5/JUajCgEIwwPfqjhQzFBgIrTH5RIOy+xXG1rTuc3meseHkNfXVeRFQl775P4oQ0pi1byRaIMyrSTXAkRVzQENQ==; 5:8nOEwG4aEQl2+w3ajJWJCexverK3VeKvRRO9ZQhiJb07QXfgwF/BdjpNkN6Z3yKefV6Baem/ghNchGYZb70HCwMv5RbfXlgWPOR6vdBdRz5VmQn5AZFq7BN+QPdmUxCusM4hQNo8tNnscCv2AfhMDGy0sx6BzjQULMZWEbBk/Uw=; 24:oaf/Jkti2D7RaUG026IoogEeGwt/bP7sNJxKIn72SpOU/QCfjvGEyAWC7tHbJZRRDTR90Eo7Wr8kU6V5ab5a/CtVxjQinczwV2YX3juenjo= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1; BY1PR0201MB1078; 7:tDeJMSTqi0l2xzDJVY/Z7QD9dOu42fR54VP7XDXdmKAFdWenJsIRgUm6UFyAOkqq/yD1g/jomlh7JfoqwDNuGyTE3nGI58QJEjuTkxlAgX/C8RBtkFHlZMr24QU32IHHqPF4oAQkpSE/zOViuL7sf/Ut+x7HGBFVGaGaivnhv1xhXZYzHUTwOU0X7befQHX97y1RM2+IvPK/Tnj6tIZZnF+ecTUkvSfXsP/St8oMUJoqsue6Pp7oDtuhlNroCtnowonEoaYX5RmDrBdJhIYz5xpzp58MZb97LvkoTK2U+aT+3efDEHgIUmvWWHX86++fbILSrkd+gyw+/Lomnfdo/5ZlKbY8MTTRYXr0pGmTS2OarDH1TjN7IQe7Tu58Nb90l25WkvHQwk+gsoiC2xmU4ZMW1SQYwF4nDOdwsIXDLQoPIF+CGex5MA8sVtPo7rM14XIVls1/XOJbhE1B41/vLg== X-OriginatorOrg: xilinx.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 04 Jan 2017 13:23:16.6443 (UTC) X-MS-Exchange-CrossTenant-Id: 657af505-d5df-48d0-8300-c31994686c5c X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=657af505-d5df-48d0-8300-c31994686c5c; Ip=[149.199.60.83]; Helo=[xsj-pvapsmtpgw01] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: BY1PR0201MB1078 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20170104_132343_445881_4CB0BE89 X-CRM114-Status: GOOD ( 16.64 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: anuragku@xilinx.com, anirudh@xilinx.com Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP This driver will add support for USB 2.0 OTG and USB 3.0 DRD in DWC3 framework. This OTG driver supports host/peripheral modes on run time. This driver will enable HNP and SRP support in High-Speed mode. Signed-off-by: Manish Narani --- drivers/usb/dwc3/otg.c | 2064 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/usb/dwc3/otg.h | 247 ++++++ 2 files changed, 2311 insertions(+) create mode 100644 drivers/usb/dwc3/otg.c create mode 100644 drivers/usb/dwc3/otg.h diff --git a/drivers/usb/dwc3/otg.c b/drivers/usb/dwc3/otg.c new file mode 100644 index 0000000..4f2aa891 --- /dev/null +++ b/drivers/usb/dwc3/otg.c @@ -0,0 +1,2064 @@ +/** + * otg.c - DesignWare USB3 DRD Controller OTG file + * + * Copyright (C) 2016 Xilinx, Inc. All rights reserved. + * + * Author: Manish Narani + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include <../drivers/usb/host/xhci.h> +#include "core.h" +#include "gadget.h" +#include "io.h" +#include "otg.h" + +#include +#include +#include "debug.h" + +/* Print the hardware registers' value for debugging purpose */ +static void print_debug_regs(struct dwc3_otg *otg) +{ + u32 gctl = otg_read(otg, DWC3_GCTL); + u32 gsts = otg_read(otg, DWC3_GSTS); + u32 gdbgltssm = otg_read(otg, DWC3_GDBGLTSSM); + u32 gusb2phycfg0 = otg_read(otg, DWC3_GUSB2PHYCFG(0)); + u32 gusb3pipectl0 = otg_read(otg, DWC3_GUSB3PIPECTL(0)); + u32 dcfg = otg_read(otg, DWC3_DCFG); + u32 dctl = otg_read(otg, DWC3_DCTL); + u32 dsts = otg_read(otg, DWC3_DSTS); + u32 ocfg = otg_read(otg, OCFG); + u32 octl = otg_read(otg, OCTL); + u32 oevt = otg_read(otg, OEVT); + u32 oevten = otg_read(otg, OEVTEN); + u32 osts = otg_read(otg, OSTS); + + otg_info(otg, "gctl = %08x\n", gctl); + otg_info(otg, "gsts = %08x\n", gsts); + otg_info(otg, "gdbgltssm = %08x\n", gdbgltssm); + otg_info(otg, "gusb2phycfg0 = %08x\n", gusb2phycfg0); + otg_info(otg, "gusb3pipectl0 = %08x\n", gusb3pipectl0); + otg_info(otg, "dcfg = %08x\n", dcfg); + otg_info(otg, "dctl = %08x\n", dctl); + otg_info(otg, "dsts = %08x\n", dsts); + otg_info(otg, "ocfg = %08x\n", ocfg); + otg_info(otg, "octl = %08x\n", octl); + otg_info(otg, "oevt = %08x\n", oevt); + otg_info(otg, "oevten = %08x\n", oevten); + otg_info(otg, "osts = %08x\n", osts); +} + +/* Check whether the hardware supports HNP or not */ +static int hnp_capable(struct dwc3_otg *otg) +{ + if (otg->hwparams6 & GHWPARAMS6_HNP_SUPPORT_ENABLED) + return 1; + return 0; +} + +/* Check whether the hardware supports SRP or not */ +static int srp_capable(struct dwc3_otg *otg) +{ + if (otg->hwparams6 & GHWPARAMS6_SRP_SUPPORT_ENABLED) + return 1; + return 0; +} + +/* Wakeup main thread to execute the OTG flow after an event */ +static void wakeup_main_thread(struct dwc3_otg *otg) +{ + if (!otg->main_thread) + return; + + otg_vdbg(otg, "\n"); + /* Tell the main thread that something has happened */ + otg->main_wakeup_needed = 1; + wake_up_interruptible(&otg->main_wq); +} + +/* Sleep main thread for 'msecs' to wait for an event to occur */ +static int sleep_main_thread_timeout(struct dwc3_otg *otg, int msecs) +{ + signed long jiffies; + int rc = msecs; + + if (signal_pending(current)) { + otg_dbg(otg, "Main thread signal pending\n"); + rc = -EINTR; + goto done; + } + if (otg->main_wakeup_needed) { + otg_dbg(otg, "Main thread wakeup needed\n"); + rc = msecs; + goto done; + } + + jiffies = msecs_to_jiffies(msecs); + rc = wait_event_freezable_timeout(otg->main_wq, + otg->main_wakeup_needed, + jiffies); + + if (rc > 0) + rc = jiffies_to_msecs(rc); + +done: + otg->main_wakeup_needed = 0; + return rc; +} + +/* Sleep main thread to wait for an event to occur */ +static int sleep_main_thread(struct dwc3_otg *otg) +{ + int rc; + + do { + rc = sleep_main_thread_timeout(otg, 5000); + } while (rc == 0); + + return rc; +} + +static void get_events(struct dwc3_otg *otg, u32 *otg_events, u32 *user_events) +{ + unsigned long flags; + + spin_lock_irqsave(&otg->lock, flags); + + if (otg_events) + *otg_events = otg->otg_events; + + if (user_events) + *user_events = otg->user_events; + + spin_unlock_irqrestore(&otg->lock, flags); +} + +static void get_and_clear_events(struct dwc3_otg *otg, u32 *otg_events, + u32 *user_events) +{ + unsigned long flags; + + spin_lock_irqsave(&otg->lock, flags); + + if (otg_events) + *otg_events = otg->otg_events; + + if (user_events) + *user_events = otg->user_events; + + otg->otg_events = 0; + otg->user_events = 0; + + spin_unlock_irqrestore(&otg->lock, flags); +} + +static int check_event(struct dwc3_otg *otg, u32 otg_mask, u32 user_mask) +{ + u32 otg_events; + u32 user_events; + + get_events(otg, &otg_events, &user_events); + if ((otg_events & otg_mask) || (user_events & user_mask)) { + otg_dbg(otg, "Event occurred: otg_events=%x, otg_mask=%x, ", + otg_events, otg_mask); + otg_dbg(otg, "user_events=%x, user_mask=%x\n", + user_events, user_mask); + return 1; + } + + return 0; +} + +static int sleep_until_event(struct dwc3_otg *otg, u32 otg_mask, u32 user_mask, + u32 *otg_events, u32 *user_events, int timeout) +{ + int rc; + + /* Enable the events */ + if (otg_mask) + otg_write(otg, OEVTEN, otg_mask); + + /* Wait until it occurs, or timeout, or interrupt. */ + if (timeout) { + otg_vdbg(otg, "Waiting for event (timeout=%d)...\n", timeout); + rc = sleep_main_thread_until_condition_timeout(otg, + check_event(otg, otg_mask, user_mask), timeout); + } else { + otg_vdbg(otg, "Waiting for event (no timeout)...\n"); + rc = sleep_main_thread_until_condition(otg, + check_event(otg, otg_mask, user_mask)); + } + + /* Disable the events */ + otg_write(otg, OEVTEN, 0); + + otg_vdbg(otg, "Woke up rc=%d\n", rc); + if (rc >= 0) + get_and_clear_events(otg, otg_events, user_events); + + return rc; +} + +static void set_capabilities(struct dwc3_otg *otg) +{ + u32 ocfg = 0; + + otg_dbg(otg, "\n"); + if (srp_capable(otg)) + ocfg |= OCFG_SRP_CAP; + + if (hnp_capable(otg)) + ocfg |= OCFG_HNP_CAP; + + otg_write(otg, OCFG, ocfg); + + otg_dbg(otg, "Enabled SRP and HNP capabilities in OCFG\n"); +} + +static int otg3_handshake(struct dwc3_otg *otg, u32 reg, u32 mask, u32 done, + u32 msec) +{ + u32 result; + u32 usec = msec * 1000; + + otg_vdbg(otg, "reg=%08x, mask=%08x, value=%08x\n", reg, mask, done); + do { + result = otg_read(otg, reg); + if ((result & mask) == done) + return 1; + udelay(1); + usec -= 1; + } while (usec > 0); + + return 0; +} + +static int reset_port(struct dwc3_otg *otg) +{ + otg_dbg(otg, "\n"); + if (!otg->otg.host) + return -ENODEV; + return usb_bus_start_enum(otg->otg.host, 1); +} + +static int set_peri_mode(struct dwc3_otg *otg, int mode) +{ + u32 octl; + + /* Set peri_mode */ + octl = otg_read(otg, OCTL); + if (mode) + octl |= OCTL_PERI_MODE; + else + octl &= ~OCTL_PERI_MODE; + + otg_write(otg, OCTL, octl); + otg_dbg(otg, "set OCTL PERI_MODE = %d in OCTL\n", mode); + + if (mode) + return otg3_handshake(otg, OSTS, OSTS_PERIP_MODE, + OSTS_PERIP_MODE, 100); + else + return otg3_handshake(otg, OSTS, OSTS_PERIP_MODE, 0, 100); + + msleep(20); +} + +static int start_host(struct dwc3_otg *otg) +{ + int ret = -ENODEV; + int flg; + u32 octl; + u32 osts; + u32 dctl; + u32 event_addr; + struct usb_hcd *hcd; + struct xhci_hcd *xhci; + + otg_dbg(otg, "\n"); + + if (!otg->otg.host) + return -ENODEV; + + dctl = otg_read(otg, DCTL); + if (dctl & DWC3_DCTL_RUN_STOP) { + otg_dbg(otg, "Disabling the RUN/STOP bit\n"); + dctl &= ~DWC3_DCTL_RUN_STOP; + otg_write(otg, DCTL, dctl); + } + + event_addr = dwc3_readl(otg->dwc->regs, DWC3_GEVNTADRLO(0)); + if (event_addr != 0x0) { + otg_dbg(otg, "Freeing the device event buffers\n"); + dwc3_free_event_buffers(otg->dwc); + } + + if (!set_peri_mode(otg, PERI_MODE_HOST)) { + otg_err(otg, "Failed to start host\n"); + return -EINVAL; + } + + hcd = container_of(otg->otg.host, struct usb_hcd, self); + xhci = hcd_to_xhci(hcd); + otg_dbg(otg, "hcd=%p xhci=%p\n", hcd, xhci); + + if (otg->host_started) { + otg_info(otg, "Host already started\n"); + goto skip; + } + + /* Start host driver */ + + *(struct xhci_hcd **)hcd->hcd_priv = xhci; + otg_dbg(otg, "1- calling usb_add_hcd() irq=%d\n", otg->hcd_irq); + ret = usb_add_hcd(hcd, otg->hcd_irq, IRQF_SHARED); + if (ret) { + otg_err(otg, "%s: failed to start primary hcd, ret=%d\n", + __func__, ret); + return ret; + } + + *(struct xhci_hcd **)xhci->shared_hcd->hcd_priv = xhci; + if (xhci->shared_hcd) { + otg_dbg(otg, "2- calling usb_add_hcd() irq=%d\n", otg->hcd_irq); + ret = usb_add_hcd(xhci->shared_hcd, otg->hcd_irq, IRQF_SHARED); + if (ret) { + otg_err(otg, + "%s: failed to start secondary hcd, ret=%d\n", + __func__, ret); + usb_remove_hcd(hcd); + return ret; + } + } + + otg->host_started = 1; +skip: + hcd->self.otg_port = 1; + if (xhci->shared_hcd) + xhci->shared_hcd->self.otg_port = 1; + + set_capabilities(otg); + + /* Power the port only for A-host */ + if (otg->otg.state == OTG_STATE_A_WAIT_VRISE) { + /* Spin on xhciPrtPwr bit until it becomes 1 */ + osts = otg_read(otg, OSTS); + flg = otg3_handshake(otg, OSTS, + OSTS_XHCI_PRT_PWR, + OSTS_XHCI_PRT_PWR, + 1000); + if (flg) { + otg_dbg(otg, "Port is powered by xhci-hcd\n"); + /* Set port power control bit */ + octl = otg_read(otg, OCTL); + octl |= OCTL_PRT_PWR_CTL; + otg_write(otg, OCTL, octl); + } else { + otg_dbg(otg, "Port is not powered by xhci-hcd\n"); + } + } + + return ret; +} + +static int stop_host(struct dwc3_otg *otg) +{ + struct usb_hcd *hcd; + struct xhci_hcd *xhci; + + otg_dbg(otg, "\n"); + + if (!otg->host_started) { + otg_info(otg, "Host already stopped\n"); + return 1; + } + + if (!otg->otg.host) + return -ENODEV; + + otg_dbg(otg, "%s: turn off host %s\n", + __func__, otg->otg.host->bus_name); + + hcd = container_of(otg->otg.host, struct usb_hcd, self); + xhci = hcd_to_xhci(hcd); + + if (xhci->shared_hcd) + usb_remove_hcd(xhci->shared_hcd); + usb_remove_hcd(hcd); + + otg->host_started = 0; + otg->dev_enum = 0; + return 0; +} + +int dwc3_otg_host_release(struct usb_hcd *hcd) +{ + struct usb_bus *bus; + struct usb_device *rh; + struct usb_device *udev; + + if (!hcd) + return -EINVAL; + + bus = &hcd->self; + if (!bus->otg_port) + return 0; + + rh = bus->root_hub; + udev = usb_hub_find_child(rh, bus->otg_port); + if (!udev) + return 0; + + if (udev->config && udev->parent == udev->bus->root_hub) { + struct usb_otg20_descriptor *desc; + + if (__usb_get_extra_descriptor(udev->rawdescriptors[0], + le16_to_cpu(udev->config[0].desc.wTotalLength), + USB_DT_OTG, (void **) &desc) == 0) { + int err; + + dev_info(&udev->dev, "found OTG descriptor\n"); + if ((desc->bcdOTG >= 0x0200) && + (udev->speed == USB_SPEED_HIGH)) { + err = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, 0, + USB_DEVICE_TEST_MODE, + 7 << 8, + NULL, 0, USB_CTRL_SET_TIMEOUT); + if (err < 0) { + dev_info(&udev->dev, + "can't initiate HNP from host: %d\n", + err); + return -1; + } + } + } else { + dev_info(&udev->dev, "didn't find OTG descriptor\n"); + } + } else { + dev_info(&udev->dev, + "udev->config NULL or udev->parent != udev->bus->root_hub\n"); + } + + return 0; +} + +/* Sends the host release set feature request */ +static void host_release(struct dwc3_otg *otg) +{ + struct usb_hcd *hcd; + struct xhci_hcd *xhci; + + otg_dbg(otg, "\n"); + if (!otg->otg.host) + return; + hcd = container_of(otg->otg.host, struct usb_hcd, self); + xhci = hcd_to_xhci(hcd); + dwc3_otg_host_release(hcd); + if (xhci->shared_hcd) + dwc3_otg_host_release(xhci->shared_hcd); +} + +static void start_peripheral(struct dwc3_otg *otg) +{ + struct usb_gadget *gadget = otg->otg.gadget; + u32 event_addr; + + otg_dbg(otg, "\n"); + if (!gadget) + return; + + if (!set_peri_mode(otg, PERI_MODE_PERIPHERAL)) + otg_err(otg, "Failed to set peripheral mode\n"); + + if (otg->peripheral_started) { + otg_info(otg, "Peripheral already started\n"); + return; + } + + event_addr = dwc3_readl(otg->dwc->regs, DWC3_GEVNTADRLO(0)); + if (event_addr == 0x0) { + int ret; + + otg_dbg(otg, "allocating the event buffer\n"); + ret = dwc3_alloc_event_buffers(otg->dwc, + DWC3_EVENT_BUFFERS_SIZE); + if (ret) { + dev_err(otg->dwc->dev, + "failed to allocate event buffers\n"); + } + otg_dbg(otg, "setting up event buffers\n"); + dwc3_event_buffers_setup(otg->dwc); + } + + otg->peripheral_started = 1; + + gadget->ops->udc_start(gadget, NULL); + + gadget->b_hnp_enable = 0; + gadget->host_request_flag = 0; + + otg_write(otg, DCTL, otg_read(otg, DCTL) | DCTL_RUN_STOP); + otg_dbg(otg, "Setting DCTL_RUN_STOP to 1 in DCTL\n"); + + msleep(20); +} + +static void stop_peripheral(struct dwc3_otg *otg) +{ + struct usb_gadget *gadget = otg->otg.gadget; + + otg_dbg(otg, "\n"); + + if (!otg->peripheral_started) { + otg_info(otg, "Peripheral already stopped\n"); + return; + } + + if (!gadget) + return; + + gadget->ops->udc_stop(gadget); + otg->peripheral_started = 0; + msleep(20); +} + +static void set_b_host(struct dwc3_otg *otg, int val) +{ + otg->otg.host->is_b_host = val; +} + +static enum usb_otg_state do_b_idle(struct dwc3_otg *otg); + +static int init_b_device(struct dwc3_otg *otg) +{ + otg_dbg(otg, "\n"); + set_capabilities(otg); + + if (!set_peri_mode(otg, PERI_MODE_PERIPHERAL)) + otg_err(otg, "Failed to start peripheral\n"); + + return do_b_idle(otg); +} + +static int init_a_device(struct dwc3_otg *otg) +{ + otg_write(otg, OCFG, 0); + otg_write(otg, OCTL, 0); + + otg_dbg(otg, "Write 0 to OCFG and OCTL\n"); + return OTG_STATE_A_IDLE; +} + +static enum usb_otg_state do_connector_id_status(struct dwc3_otg *otg) +{ + enum usb_otg_state state; + u32 osts; + + otg_dbg(otg, "\n"); + + otg_write(otg, OCFG, 0); + otg_write(otg, OEVTEN, 0); + otg_write(otg, OEVT, 0xffffffff); + otg_write(otg, OEVTEN, OEVT_CONN_ID_STS_CHNG_EVNT); + + msleep(60); + + osts = otg_read(otg, OSTS); + if (!(osts & OSTS_CONN_ID_STS)) { + otg_dbg(otg, "Connector ID is A\n"); + state = init_a_device(otg); + } else { + otg_dbg(otg, "Connector ID is B\n"); + stop_host(otg); + state = init_b_device(otg); + } + + /* TODO: This is a workaround for latest hibernation-enabled bitfiles + * which have problems before initializing SRP. + */ + msleep(50); + + return state; +} + +static void reset_hw(struct dwc3_otg *otg) +{ + u32 temp; + + otg_dbg(otg, "\n"); + + otg_write(otg, OEVTEN, 0); + temp = otg_read(otg, OCTL); + temp &= OCTL_PERI_MODE; + otg_write(otg, OCTL, temp); + temp = otg_read(otg, GCTL); + temp |= GCTL_PRT_CAP_DIR_OTG << GCTL_PRT_CAP_DIR_SHIFT; + otg_write(otg, GCTL, temp); +} + +#define SRP_TIMEOUT 6000 + +static void start_srp(struct dwc3_otg *otg) +{ + u32 octl; + + octl = otg_read(otg, OCTL); + octl |= OCTL_SES_REQ; + otg_write(otg, OCTL, octl); + otg_dbg(otg, "set OCTL_SES_REQ in OCTL\n"); +} + +static void start_b_hnp(struct dwc3_otg *otg) +{ + u32 octl; + + octl = otg_read(otg, OCTL); + octl |= OCTL_HNP_REQ | OCTL_DEV_SET_HNP_EN; + otg_write(otg, OCTL, octl); + otg_dbg(otg, "set (OCTL_HNP_REQ | OCTL_DEV_SET_HNP_EN) in OCTL\n"); +} + +static void stop_b_hnp(struct dwc3_otg *otg) +{ + u32 octl; + + octl = otg_read(otg, OCTL); + octl &= ~(OCTL_HNP_REQ | OCTL_DEV_SET_HNP_EN); + otg_write(otg, OCTL, octl); + otg_dbg(otg, "Clear ~(OCTL_HNP_REQ | OCTL_DEV_SET_HNP_EN) in OCTL\n"); +} + +static void start_a_hnp(struct dwc3_otg *otg) +{ + u32 octl; + + octl = otg_read(otg, OCTL); + octl |= OCTL_HST_SET_HNP_EN; + otg_write(otg, OCTL, octl); + otg_dbg(otg, "set OCTL_HST_SET_HNP_EN in OCTL\n"); +} + +static void stop_a_hnp(struct dwc3_otg *otg) +{ + u32 octl; + + octl = otg_read(otg, OCTL); + octl &= ~OCTL_HST_SET_HNP_EN; + otg_write(otg, OCTL, octl); + otg_dbg(otg, "clear OCTL_HST_SET_HNP_EN in OCTL\n"); +} + +static enum usb_otg_state do_a_hnp_init(struct dwc3_otg *otg) +{ + int rc; + u32 otg_mask; + u32 otg_events = 0; + + otg_dbg(otg, ""); + otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT | + OEVT_A_DEV_HNP_CHNG_EVNT; + + start_a_hnp(otg); + rc = 3000; + +again: + rc = sleep_until_event(otg, + otg_mask, 0, + &otg_events, NULL, rc); + stop_a_hnp(otg); + if (rc < 0) + return OTG_STATE_UNDEFINED; + + /* Higher priority first */ + if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) { + otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n"); + return OTG_STATE_UNDEFINED; + + } else if (otg_events & OEVT_A_DEV_HNP_CHNG_EVNT) { + otg_dbg(otg, "OEVT_A_DEV_HNP_CHNG_EVNT\n"); + if (otg_events & OEVT_HST_NEG_SCS) { + otg_dbg(otg, "A-HNP Success\n"); + return OTG_STATE_A_PERIPHERAL; + + } else { + otg_dbg(otg, "A-HNP Failed\n"); + return OTG_STATE_A_WAIT_VFALL; + } + + } else if (rc == 0) { + otg_dbg(otg, "A-HNP Failed (Timed out)\n"); + return OTG_STATE_A_WAIT_VFALL; + + } else { + goto again; + } + + /* Invalid state */ + return OTG_STATE_UNDEFINED; +} + +static enum usb_otg_state do_a_host(struct dwc3_otg *otg) +{ + int rc; + u32 otg_mask; + u32 user_mask; + u32 otg_events = 0; + u32 user_events = 0; + + otg_dbg(otg, ""); + + otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT | + OEVT_A_DEV_SESS_END_DET_EVNT; + user_mask = USER_SRP_EVENT | + USER_HNP_EVENT; + + rc = sleep_until_event(otg, + otg_mask, user_mask, + &otg_events, &user_events, 0); + if (rc < 0) + return OTG_STATE_UNDEFINED; + + /* Higher priority first */ + if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) { + otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n"); + return OTG_STATE_UNDEFINED; + + } else if (otg_events & OEVT_A_DEV_SESS_END_DET_EVNT) { + otg_dbg(otg, "OEVT_A_DEV_SESS_END_DET_EVNT\n"); + return OTG_STATE_A_WAIT_VFALL; + + } else if (user_events & USER_HNP_EVENT) { + otg_dbg(otg, "USER_HNP_EVENT\n"); + return OTG_STATE_A_SUSPEND; + } + + /* Invalid state */ + return OTG_STATE_UNDEFINED; +} + +#define A_WAIT_VFALL_TIMEOUT 1000 + +static enum usb_otg_state do_a_wait_vfall(struct dwc3_otg *otg) +{ + int rc; + u32 otg_mask; + u32 otg_events = 0; + + otg_dbg(otg, ""); + + otg_mask = OEVT_A_DEV_IDLE_EVNT; + + rc = A_WAIT_VFALL_TIMEOUT; + rc = sleep_until_event(otg, + otg_mask, 0, + &otg_events, NULL, rc); + if (rc < 0) + return OTG_STATE_UNDEFINED; + + if (otg_events & OEVT_A_DEV_IDLE_EVNT) { + otg_dbg(otg, "OEVT_A_DEV_IDLE_EVNT\n"); + return OTG_STATE_A_IDLE; + + } else if (rc == 0) { + otg_dbg(otg, "A_WAIT_VFALL_TIMEOUT\n"); + return OTG_STATE_A_IDLE; + } + + /* Invalid state */ + return OTG_STATE_UNDEFINED; + +} + +#define A_WAIT_BCON_TIMEOUT 1000 + +static enum usb_otg_state do_a_wait_bconn(struct dwc3_otg *otg) +{ + int rc; + u32 otg_mask; + u32 otg_events = 0; + + otg_dbg(otg, ""); + + otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT | + OEVT_A_DEV_SESS_END_DET_EVNT | + OEVT_A_DEV_HOST_EVNT; + + rc = A_WAIT_BCON_TIMEOUT; + rc = sleep_until_event(otg, + otg_mask, 0, + &otg_events, NULL, rc); + if (rc < 0) + return OTG_STATE_UNDEFINED; + + /* Higher priority first */ + if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) { + otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n"); + return OTG_STATE_UNDEFINED; + + } else if (otg_events & OEVT_A_DEV_SESS_END_DET_EVNT) { + otg_dbg(otg, "OEVT_A_DEV_SESS_END_DET_EVNT\n"); + return OTG_STATE_A_WAIT_VFALL; + + } else if (otg_events & OEVT_A_DEV_HOST_EVNT) { + otg_dbg(otg, "OEVT_A_DEV_HOST_EVNT\n"); + return OTG_STATE_A_HOST; + + } else if (rc == 0) { + if (otg_read(otg, OCTL) & OCTL_PRT_PWR_CTL) + return OTG_STATE_A_HOST; + else + return OTG_STATE_A_WAIT_VFALL; + } + + /* Invalid state */ + return OTG_STATE_UNDEFINED; +} + +#define A_WAIT_VRISE_TIMEOUT 100 + +static enum usb_otg_state do_a_wait_vrise(struct dwc3_otg *otg) +{ + int rc; + u32 otg_mask; + u32 otg_events = 0; + struct usb_hcd *hcd; + struct xhci_hcd *xhci; + + otg_dbg(otg, ""); + set_b_host(otg, 0); + start_host(otg); + hcd = container_of(otg->otg.host, struct usb_hcd, self); + xhci = hcd_to_xhci(hcd); + usb_kick_hub_wq(hcd->self.root_hub); + if (xhci->shared_hcd) + usb_kick_hub_wq(xhci->shared_hcd->self.root_hub); + + otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT | + OEVT_A_DEV_SESS_END_DET_EVNT; + + rc = A_WAIT_VRISE_TIMEOUT; + + rc = sleep_until_event(otg, + otg_mask, 0, + &otg_events, NULL, rc); + if (rc < 0) + return OTG_STATE_UNDEFINED; + + /* Higher priority first */ + if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) { + otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n"); + return OTG_STATE_UNDEFINED; + + } else if (otg_events & OEVT_A_DEV_SESS_END_DET_EVNT) { + otg_dbg(otg, "OEVT_A_DEV_SESS_END_DET_EVNT\n"); + return OTG_STATE_A_WAIT_VFALL; + + } else if (rc == 0) { + if (otg_read(otg, OCTL) & OCTL_PRT_PWR_CTL) + return OTG_STATE_A_WAIT_BCON; + else + return OTG_STATE_A_WAIT_VFALL; + } + + /* Invalid state */ + return OTG_STATE_UNDEFINED; +} + +static enum usb_otg_state do_a_idle(struct dwc3_otg *otg) +{ + int rc; + u32 otg_mask; + u32 user_mask; + u32 otg_events = 0; + u32 user_events = 0; + + otg_dbg(otg, ""); + + otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT | OEVT_A_DEV_SRP_DET_EVNT; + user_mask = USER_SRP_EVENT; + + rc = sleep_until_event(otg, + otg_mask, user_mask, + &otg_events, &user_events, + 0); + + if (rc < 0) + return OTG_STATE_UNDEFINED; + + if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) { + otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n"); + return OTG_STATE_UNDEFINED; + } else if (otg_events & OEVT_A_DEV_SRP_DET_EVNT) { + otg_dbg(otg, "OEVT_A_DEV_SRP_DET_EVNT\n"); + return OTG_STATE_A_WAIT_VRISE; + } else if (user_events & USER_SRP_EVENT) { + otg_dbg(otg, "User initiated VBUS\n"); + return OTG_STATE_A_WAIT_VRISE; + } + + return OTG_STATE_UNDEFINED; +} + +static enum usb_otg_state do_a_peripheral(struct dwc3_otg *otg) +{ + int rc; + u32 otg_mask; + u32 otg_events = 0; + + otg_dbg(otg, ""); + otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT | + OEVT_A_DEV_SESS_END_DET_EVNT | + OEVT_A_DEV_B_DEV_HOST_END_EVNT; + + rc = sleep_until_event(otg, + otg_mask, 0, + &otg_events, NULL, 0); + if (rc < 0) + return OTG_STATE_UNDEFINED; + + if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) { + otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n"); + return OTG_STATE_UNDEFINED; + + } else if (otg_events & OEVT_A_DEV_SESS_END_DET_EVNT) { + otg_dbg(otg, "OEVT_A_DEV_SESS_END_DET_EVNT\n"); + return OTG_STATE_A_WAIT_VFALL; + + } else if (otg_events & OEVT_A_DEV_B_DEV_HOST_END_EVNT) { + otg_dbg(otg, "OEVT_A_DEV_B_DEV_HOST_END_EVNT\n"); + return OTG_STATE_A_WAIT_VRISE; + } + + return OTG_STATE_UNDEFINED; +} + +#define HNP_TIMEOUT 4000 + +static enum usb_otg_state do_b_hnp_init(struct dwc3_otg *otg) +{ + int rc; + u32 otg_mask; + u32 events = 0; + + otg_dbg(otg, ""); + otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT | + OEVT_B_DEV_HNP_CHNG_EVNT | + OEVT_B_DEV_VBUS_CHNG_EVNT; + + start_b_hnp(otg); + rc = HNP_TIMEOUT; + +again: + rc = sleep_until_event(otg, + otg_mask, 0, + &events, NULL, rc); + stop_b_hnp(otg); + + if (rc < 0) + return OTG_STATE_UNDEFINED; + + if (events & OEVT_CONN_ID_STS_CHNG_EVNT) { + otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n"); + return OTG_STATE_UNDEFINED; + } else if (events & OEVT_B_DEV_VBUS_CHNG_EVNT) { + otg_dbg(otg, "OEVT_B_DEV_VBUS_CHNG_EVNT\n"); + return OTG_STATE_B_IDLE; + } else if (events & OEVT_B_DEV_HNP_CHNG_EVNT) { + otg_dbg(otg, "OEVT_B_DEV_HNP_CHNG_EVNT\n"); + if (events & OEVT_HST_NEG_SCS) { + otg_dbg(otg, "B-HNP Success\n"); + return OTG_STATE_B_WAIT_ACON; + + } else { + otg_err(otg, "B-HNP Failed\n"); + return OTG_STATE_B_PERIPHERAL; + } + } else if (rc == 0) { + /* Timeout */ + otg_err(otg, "HNP timed out!\n"); + return OTG_STATE_B_PERIPHERAL; + + } else { + goto again; + } + + return OTG_STATE_UNDEFINED; +} + +static enum usb_otg_state do_b_peripheral(struct dwc3_otg *otg) +{ + int rc; + u32 otg_mask; + u32 user_mask; + u32 otg_events = 0; + u32 user_events = 0; + + otg_dbg(otg, ""); + otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT | OEVT_B_DEV_VBUS_CHNG_EVNT; + user_mask = USER_HNP_EVENT | USER_END_SESSION | + USER_SRP_EVENT | INITIAL_SRP; + +again: + rc = sleep_until_event(otg, + otg_mask, user_mask, + &otg_events, &user_events, 0); + if (rc < 0) + return OTG_STATE_UNDEFINED; + + if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) { + otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n"); + return OTG_STATE_UNDEFINED; + } else if (otg_events & OEVT_B_DEV_VBUS_CHNG_EVNT) { + otg_dbg(otg, "OEVT_B_DEV_VBUS_CHNG_EVNT\n"); + + if (otg_events & OEVT_B_SES_VLD_EVT) { + otg_dbg(otg, "Session valid\n"); + goto again; + } else { + otg_dbg(otg, "Session not valid\n"); + return OTG_STATE_B_IDLE; + } + + } else if (user_events & USER_HNP_EVENT) { + otg_dbg(otg, "USER_HNP_EVENT\n"); + return do_b_hnp_init(otg); + } else if (user_events & USER_END_SESSION) { + otg_dbg(otg, "USER_END_SESSION\n"); + return OTG_STATE_B_IDLE; + } + + return OTG_STATE_UNDEFINED; +} + +static enum usb_otg_state do_b_wait_acon(struct dwc3_otg *otg) +{ + int rc; + u32 otg_mask; + u32 user_mask = 0; + u32 otg_events = 0; + u32 user_events = 0; + struct usb_hcd *hcd; + struct xhci_hcd *xhci; + + otg_dbg(otg, ""); + set_b_host(otg, 1); + start_host(otg); + otg_mask = OEVT_B_DEV_B_HOST_END_EVNT; + otg_write(otg, OEVTEN, otg_mask); + reset_port(otg); + + hcd = container_of(otg->otg.host, struct usb_hcd, self); + xhci = hcd_to_xhci(hcd); + usb_kick_hub_wq(hcd->self.root_hub); + if (xhci->shared_hcd) + usb_kick_hub_wq(xhci->shared_hcd->self.root_hub); + + otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT | + OEVT_B_DEV_B_HOST_END_EVNT | + OEVT_B_DEV_VBUS_CHNG_EVNT | + OEVT_HOST_ROLE_REQ_INIT_EVNT; + user_mask = USER_A_CONN_EVENT; + +again: + rc = sleep_until_event(otg, + otg_mask, user_mask, + &otg_events, &user_events, 0); + if (rc < 0) + return OTG_STATE_UNDEFINED; + + /* Higher priority first */ + if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) { + otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n"); + return OTG_STATE_UNDEFINED; + } else if (otg_events & OEVT_B_DEV_B_HOST_END_EVNT) { + otg_dbg(otg, "OEVT_B_DEV_B_HOST_END_EVNT\n"); + return OTG_STATE_B_PERIPHERAL; + } else if (otg_events & OEVT_B_DEV_VBUS_CHNG_EVNT) { + otg_dbg(otg, "OEVT_B_DEV_VBUS_CHNG_EVNT\n"); + if (otg_events & OEVT_B_SES_VLD_EVT) { + otg_dbg(otg, "Session valid\n"); + goto again; + } else { + otg_dbg(otg, "Session not valid\n"); + return OTG_STATE_B_IDLE; + } + } else if (user_events & USER_A_CONN_EVENT) { + otg_dbg(otg, "A-device connected\n"); + return OTG_STATE_B_HOST; + } + + /* Invalid state */ + return OTG_STATE_UNDEFINED; +} + +static enum usb_otg_state do_b_host(struct dwc3_otg *otg) +{ + int rc; + u32 otg_mask; + u32 user_mask = 0; + u32 otg_events = 0; + u32 user_events = 0; + + otg_dbg(otg, ""); + + otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT | + OEVT_B_DEV_B_HOST_END_EVNT | + OEVT_B_DEV_VBUS_CHNG_EVNT | + OEVT_HOST_ROLE_REQ_INIT_EVNT; + +again: + rc = sleep_until_event(otg, + otg_mask, user_mask, + &otg_events, &user_events, 0); + if (rc < 0) + return OTG_STATE_UNDEFINED; + + /* Higher priority first */ + if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) { + otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n"); + return OTG_STATE_UNDEFINED; + } else if (otg_events & OEVT_B_DEV_B_HOST_END_EVNT) { + otg_dbg(otg, "OEVT_B_DEV_B_HOST_END_EVNT\n"); + return OTG_STATE_B_PERIPHERAL; + } else if (otg_events & OEVT_B_DEV_VBUS_CHNG_EVNT) { + otg_dbg(otg, "OEVT_B_DEV_VBUS_CHNG_EVNT\n"); + if (otg_events & OEVT_B_SES_VLD_EVT) { + otg_dbg(otg, "Session valid\n"); + goto again; + } else { + otg_dbg(otg, "Session not valid\n"); + return OTG_STATE_B_IDLE; + } + } + + /* Invalid state */ + return OTG_STATE_UNDEFINED; +} + +static enum usb_otg_state do_b_idle(struct dwc3_otg *otg) +{ + int rc; + u32 otg_mask; + u32 user_mask; + u32 otg_events = 0; + u32 user_events = 0; + + otg_dbg(otg, ""); + otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT | + OEVT_B_DEV_SES_VLD_DET_EVNT | + OEVT_B_DEV_VBUS_CHNG_EVNT; + user_mask = USER_SRP_EVENT; + +again: + rc = sleep_until_event(otg, + otg_mask, user_mask, + &otg_events, &user_events, 0); + + if (rc < 0) + return OTG_STATE_UNDEFINED; + + if (otg_events & OEVT_CONN_ID_STS_CHNG_EVNT) { + otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n"); + return OTG_STATE_UNDEFINED; + } else if ((otg_events & OEVT_B_DEV_VBUS_CHNG_EVNT) || + (otg_events & OEVT_B_DEV_SES_VLD_DET_EVNT)) { + otg_dbg(otg, "OEVT_B_DEV_VBUS_CHNG_EVNT\n"); + if (otg_events & OEVT_B_SES_VLD_EVT) { + otg_dbg(otg, "Session valid\n"); + return OTG_STATE_B_PERIPHERAL; + + } else { + otg_dbg(otg, "Session not valid\n"); + goto again; + } + } else if (user_events & USER_SRP_EVENT) { + otg_dbg(otg, "USER_SRP_EVENT\n"); + return OTG_STATE_B_SRP_INIT; + } + + return OTG_STATE_UNDEFINED; +} + +static enum usb_otg_state do_b_srp_init(struct dwc3_otg *otg) +{ + int rc; + u32 otg_mask; + u32 events = 0; + + otg_dbg(otg, ""); + otg_mask = OEVT_CONN_ID_STS_CHNG_EVNT | + OEVT_B_DEV_SES_VLD_DET_EVNT | + OEVT_B_DEV_VBUS_CHNG_EVNT; + + otg_write(otg, OEVTEN, otg_mask); + start_srp(otg); + + rc = SRP_TIMEOUT; + +again: + rc = sleep_until_event(otg, + otg_mask, 0, + &events, NULL, rc); + if (rc < 0) + return OTG_STATE_UNDEFINED; + + if (events & OEVT_CONN_ID_STS_CHNG_EVNT) { + otg_dbg(otg, "OEVT_CONN_ID_STS_CHNG_EVNT\n"); + return OTG_STATE_UNDEFINED; + } else if (events & OEVT_B_DEV_SES_VLD_DET_EVNT) { + otg_dbg(otg, "OEVT_B_DEV_SES_VLD_DET_EVNT\n"); + return OTG_STATE_B_PERIPHERAL; + } else if (rc == 0) { + otg_dbg(otg, "SRP Timeout (rc=%d)\n", rc); + otg_info(otg, "DEVICE NO RESPONSE FOR SRP\n"); + return OTG_STATE_B_IDLE; + + } else { + goto again; + } + + return OTG_STATE_UNDEFINED; +} + +int otg_main_thread(void *data) +{ + struct dwc3_otg *otg = (struct dwc3_otg *)data; + enum usb_otg_state prev = OTG_STATE_UNDEFINED; + +#ifdef VERBOSE_DEBUG + u32 snpsid = otg_read(otg, 0xc120); + + otg_vdbg(otg, "io_priv=%p\n", otg->regs); + otg_vdbg(otg, "c120: %x\n", snpsid); +#endif + + /* Allow the thread to be killed by a signal, but set the signal mask + * to block everything but INT, TERM, KILL, and USR1. + */ + allow_signal(SIGINT); + allow_signal(SIGTERM); + allow_signal(SIGKILL); + allow_signal(SIGUSR1); + + /* Allow the thread to be frozen */ + set_freezable(); + + /* Allow host/peripheral driver load to finish */ + msleep(100); + + reset_hw(otg); + + stop_host(otg); + stop_peripheral(otg); + + otg_dbg(otg, "Thread running\n"); + while (1) { + enum usb_otg_state next = OTG_STATE_UNDEFINED; + + otg_vdbg(otg, "\n\n\nMain thread entering state\n"); + + switch (otg->otg.state) { + case OTG_STATE_UNDEFINED: + otg_dbg(otg, "OTG_STATE_UNDEFINED\n"); + next = do_connector_id_status(otg); + break; + + case OTG_STATE_A_IDLE: + otg_dbg(otg, "OTG_STATE_A_IDLE\n"); + stop_peripheral(otg); + + if (prev == OTG_STATE_UNDEFINED) + next = OTG_STATE_A_WAIT_VRISE; + else + next = do_a_idle(otg); + break; + + case OTG_STATE_A_WAIT_VRISE: + otg_dbg(otg, "OTG_STATE_A_WAIT_VRISE\n"); + next = do_a_wait_vrise(otg); + break; + + case OTG_STATE_A_WAIT_BCON: + otg_dbg(otg, "OTG_STATE_A_WAIT_BCON\n"); + next = do_a_wait_bconn(otg); + break; + + case OTG_STATE_A_HOST: + otg_dbg(otg, "OTG_STATE_A_HOST\n"); + stop_peripheral(otg); + next = do_a_host(otg); + /* Don't stop the host here if we are going into + * A_SUSPEND. We need to delay that until later. It + * will be stopped when coming out of A_SUSPEND + * state. + */ + if (next != OTG_STATE_A_SUSPEND) + stop_host(otg); + break; + + case OTG_STATE_A_SUSPEND: + otg_dbg(otg, "OTG_STATE_A_SUSPEND\n"); + next = do_a_hnp_init(otg); + + /* Stop the host. */ + stop_host(otg); + break; + + case OTG_STATE_A_WAIT_VFALL: + otg_dbg(otg, "OTG_STATE_A_WAIT_VFALL\n"); + next = do_a_wait_vfall(otg); + stop_host(otg); + break; + + case OTG_STATE_A_PERIPHERAL: + otg_dbg(otg, "OTG_STATE_A_PERIPHERAL\n"); + stop_host(otg); + start_peripheral(otg); + next = do_a_peripheral(otg); + stop_peripheral(otg); + break; + + case OTG_STATE_B_IDLE: + otg_dbg(otg, "OTG_STATE_B_IDLE\n"); + next = do_b_idle(otg); + break; + + case OTG_STATE_B_PERIPHERAL: + otg_dbg(otg, "OTG_STATE_B_PERIPHERAL\n"); + stop_host(otg); + start_peripheral(otg); + next = do_b_peripheral(otg); + stop_peripheral(otg); + break; + + case OTG_STATE_B_SRP_INIT: + otg_dbg(otg, "OTG_STATE_B_SRP_INIT\n"); + otg_read(otg, OSTS); + next = do_b_srp_init(otg); + break; + + case OTG_STATE_B_WAIT_ACON: + otg_dbg(otg, "OTG_STATE_B_WAIT_ACON\n"); + next = do_b_wait_acon(otg); + break; + + case OTG_STATE_B_HOST: + otg_dbg(otg, "OTG_STATE_B_HOST\n"); + next = do_b_host(otg); + stop_host(otg); + break; + + default: + otg_err(otg, "Unknown state %d, sleeping...\n", + otg->state); + sleep_main_thread(otg); + break; + } + + prev = otg->otg.state; + otg->otg.state = next; + if (kthread_should_stop()) + break; + } + + otg->main_thread = NULL; + otg_dbg(otg, "OTG main thread exiting....\n"); + + return 0; +} + +static void start_main_thread(struct dwc3_otg *otg) +{ + if (!otg->main_thread && otg->otg.gadget && otg->otg.host) { + otg_dbg(otg, "Starting OTG main thread\n"); + otg->main_thread = kthread_create(otg_main_thread, otg, "otg"); + wake_up_process(otg->main_thread); + } +} + +static inline struct dwc3_otg *otg_to_dwc3_otg(struct usb_otg *x) +{ + return container_of(x, struct dwc3_otg, otg); +} + +static irqreturn_t dwc3_otg_irq(int irq, void *_otg) +{ + struct dwc3_otg *otg; + u32 oevt; + u32 osts; + u32 octl; + u32 ocfg; + u32 oevten; + u32 otg_mask = OEVT_ALL; + + if (!_otg) + return 0; + + otg = (struct dwc3_otg *)_otg; + + oevt = otg_read(otg, OEVT); + osts = otg_read(otg, OSTS); + octl = otg_read(otg, OCTL); + ocfg = otg_read(otg, OCFG); + oevten = otg_read(otg, OEVTEN); + + /* Clear handled events */ + otg_write(otg, OEVT, oevt); + + otg_vdbg(otg, "\n"); + otg_vdbg(otg, " oevt = %08x\n", oevt); + otg_vdbg(otg, " osts = %08x\n", osts); + otg_vdbg(otg, " octl = %08x\n", octl); + otg_vdbg(otg, " ocfg = %08x\n", ocfg); + otg_vdbg(otg, " oevten = %08x\n", oevten); + + otg_vdbg(otg, "oevt[DeviceMode] = %s\n", + oevt & OEVT_DEV_MOD_EVNT ? "Device" : "Host"); + + if (oevt & OEVT_CONN_ID_STS_CHNG_EVNT) + otg_dbg(otg, "Connector ID Status Change Event\n"); + if (oevt & OEVT_HOST_ROLE_REQ_INIT_EVNT) + otg_dbg(otg, "Host Role Request Init Notification Event\n"); + if (oevt & OEVT_HOST_ROLE_REQ_CONFIRM_EVNT) + otg_dbg(otg, "Host Role Request Confirm Notification Event\n"); + if (oevt & OEVT_A_DEV_B_DEV_HOST_END_EVNT) + otg_dbg(otg, "A-Device B-Host End Event\n"); + if (oevt & OEVT_A_DEV_HOST_EVNT) + otg_dbg(otg, "A-Device Host Event\n"); + if (oevt & OEVT_A_DEV_HNP_CHNG_EVNT) + otg_dbg(otg, "A-Device HNP Change Event\n"); + if (oevt & OEVT_A_DEV_SRP_DET_EVNT) + otg_dbg(otg, "A-Device SRP Detect Event\n"); + if (oevt & OEVT_A_DEV_SESS_END_DET_EVNT) + otg_dbg(otg, "A-Device Session End Detected Event\n"); + if (oevt & OEVT_B_DEV_B_HOST_END_EVNT) + otg_dbg(otg, "B-Device B-Host End Event\n"); + if (oevt & OEVT_B_DEV_HNP_CHNG_EVNT) + otg_dbg(otg, "B-Device HNP Change Event\n"); + if (oevt & OEVT_B_DEV_SES_VLD_DET_EVNT) + otg_dbg(otg, "B-Device Session Valid Detect Event\n"); + if (oevt & OEVT_B_DEV_VBUS_CHNG_EVNT) + otg_dbg(otg, "B-Device VBUS Change Event\n"); + + if (oevt & otg_mask) { + /* Pass event to main thread */ + spin_lock(&otg->lock); + otg->otg_events |= oevt; + wakeup_main_thread(otg); + spin_unlock(&otg->lock); + return 1; + } + + return IRQ_HANDLED; +} + +static void hnp_polling_work(struct work_struct *w) +{ + struct dwc3_otg *otg = container_of(w, struct dwc3_otg, + hp_work.work); + struct usb_bus *bus; + struct usb_device *udev; + struct usb_hcd *hcd; + u8 *otgstatus; + int ret; + int err; + + hcd = container_of(otg->otg.host, struct usb_hcd, self); + if (!hcd) + return; + + bus = &hcd->self; + if (!bus->otg_port) + return; + + udev = usb_hub_find_child(bus->root_hub, bus->otg_port); + if (!udev) + return; + + otgstatus = kmalloc(sizeof(*otgstatus), GFP_NOIO); + if (!otgstatus) + return; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RECIP_DEVICE, + 0, 0xf000, otgstatus, sizeof(*otgstatus), + USB_CTRL_GET_TIMEOUT); + + if (ret == sizeof(*otgstatus) && (*otgstatus & 0x1)) { + /* enable HNP before suspend, it's simpler */ + + udev->bus->b_hnp_enable = 1; + err = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, 0, + udev->bus->b_hnp_enable + ? USB_DEVICE_B_HNP_ENABLE + : USB_DEVICE_A_ALT_HNP_SUPPORT, + 0, NULL, 0, USB_CTRL_SET_TIMEOUT); + + if (err < 0) { + /* OTG MESSAGE: report errors here, + * customize to match your product. + */ + otg_info(otg, "ERROR : Device no response\n"); + dev_info(&udev->dev, "can't set HNP mode: %d\n", + err); + udev->bus->b_hnp_enable = 0; + if (le16_to_cpu(udev->descriptor.idVendor) == 0x1a0a) { + if (usb_port_suspend(udev, PMSG_AUTO_SUSPEND) + < 0) + dev_dbg(&udev->dev, "HNP fail, %d\n", + err); + } + } else { + /* Device wants role-switch, suspend the bus. */ + static struct usb_phy *phy; + + phy = usb_get_phy(USB_PHY_TYPE_USB3); + otg_start_hnp(phy->otg); + usb_put_phy(phy); + + if (usb_port_suspend(udev, PMSG_AUTO_SUSPEND) < 0) + dev_dbg(&udev->dev, "HNP fail, %d\n", err); + } + } else if (ret < 0) { + udev->bus->b_hnp_enable = 1; + err = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, 0, + USB_DEVICE_B_HNP_ENABLE, + 0, NULL, 0, USB_CTRL_SET_TIMEOUT); + if (usb_port_suspend(udev, PMSG_AUTO_SUSPEND) < 0) + dev_dbg(&udev->dev, "HNP fail, %d\n", err); + } else { + schedule_delayed_work(&otg->hp_work, 1 * HZ); + } + + kfree(otgstatus); +} + +static int dwc3_otg_notify_connect(struct usb_phy *phy, + enum usb_device_speed speed) +{ + struct usb_bus *bus; + struct usb_device *udev; + struct usb_hcd *hcd; + struct dwc3_otg *otg; + int err = 0; + + otg = otg_to_dwc3_otg(phy->otg); + + hcd = container_of(phy->otg->host, struct usb_hcd, self); + if (!hcd) + return -EINVAL; + + bus = &hcd->self; + if (!bus->otg_port) + return 0; + + udev = usb_hub_find_child(bus->root_hub, bus->otg_port); + if (!udev) + return 0; + + /* + * OTG-aware devices on OTG-capable root hubs may be able to use SRP, + * to wake us after we've powered off VBUS; and HNP, switching roles + * "host" to "peripheral". The OTG descriptor helps figure this out. + */ + if (udev->config && udev->parent == udev->bus->root_hub) { + struct usb_otg20_descriptor *desc = NULL; + + /* descriptor may appear anywhere in config */ + err = __usb_get_extra_descriptor(udev->rawdescriptors[0], + le16_to_cpu(udev->config[0].desc.wTotalLength), + USB_DT_OTG, (void **) &desc); + if (err || !(desc->bmAttributes & USB_OTG_HNP)) + return 0; + + if (udev->portnum == udev->bus->otg_port) { + INIT_DELAYED_WORK(&otg->hp_work, + hnp_polling_work); + schedule_delayed_work(&otg->hp_work, HZ); + } + + } + + return err; +} + +static int dwc3_otg_notify_disconnect(struct usb_phy *phy, + enum usb_device_speed speed) +{ + struct dwc3_otg *otg; + + otg = otg_to_dwc3_otg(phy->otg); + + if (work_pending(&otg->hp_work.work)) { + while (!cancel_delayed_work(&otg->hp_work)) + msleep(20); + } + return 0; +} + +static void dwc3_otg_set_peripheral(struct usb_otg *_otg, int yes) +{ + struct dwc3_otg *otg; + + if (!_otg) + return; + + otg = otg_to_dwc3_otg(_otg); + otg_dbg(otg, "\n"); + + if (yes) { + if (otg->hwparams6 == 0xdeadbeef) + otg->hwparams6 = otg_read(otg, GHWPARAMS6); + stop_host(otg); + } else { + stop_peripheral(otg); + } + + set_peri_mode(otg, yes); +} +EXPORT_SYMBOL(dwc3_otg_set_peripheral); + +static int dwc3_otg_set_periph(struct usb_otg *_otg, struct usb_gadget *gadget) +{ + struct dwc3_otg *otg; + + if (!_otg) + return -ENODEV; + + otg = otg_to_dwc3_otg(_otg); + otg_dbg(otg, "\n"); + + if ((long)gadget == 1) { + dwc3_otg_set_peripheral(_otg, 1); + return 0; + } + + if (!gadget) { + otg->otg.gadget = NULL; + return -ENODEV; + } + + otg->otg.gadget = gadget; + otg->otg.gadget->hnp_polling_support = 1; + otg->otg.state = OTG_STATE_B_IDLE; + + start_main_thread(otg); + return 0; +} + +static int dwc3_otg_set_host(struct usb_otg *_otg, struct usb_bus *host) +{ + struct dwc3_otg *otg; + struct usb_hcd *hcd; + struct xhci_hcd *xhci; + + if (!_otg) + return -ENODEV; + + otg = otg_to_dwc3_otg(_otg); + otg_dbg(otg, "\n"); + + if ((long)host == 1) { + dwc3_otg_set_peripheral(_otg, 0); + return 0; + } + + if (!host) { + otg->otg.host = NULL; + otg->hcd_irq = 0; + return -ENODEV; + } + + hcd = container_of(host, struct usb_hcd, self); + xhci = hcd_to_xhci(hcd); + otg_dbg(otg, "hcd=%p xhci=%p\n", hcd, xhci); + + hcd->self.otg_port = 1; + if (xhci->shared_hcd) { + xhci->shared_hcd->self.otg_port = 1; + otg_dbg(otg, "shared_hcd=%p\n", xhci->shared_hcd); + } + + otg->otg.host = host; + otg->hcd_irq = hcd->irq; + otg_dbg(otg, "host=%p irq=%d\n", otg->otg.host, otg->hcd_irq); + + + otg->host_started = 1; + otg->dev_enum = 0; + start_main_thread(otg); + return 0; +} + +static int dwc3_otg_start_srp(struct usb_otg *x) +{ + unsigned long flags; + struct dwc3_otg *otg; + + if (!x) + return -ENODEV; + + otg = otg_to_dwc3_otg(x); + otg_dbg(otg, "\n"); + + if (!otg->otg.host || !otg->otg.gadget) + return -ENODEV; + + spin_lock_irqsave(&otg->lock, flags); + otg->user_events |= USER_SRP_EVENT; + wakeup_main_thread(otg); + spin_unlock_irqrestore(&otg->lock, flags); + return 0; +} + +static int dwc3_otg_start_hnp(struct usb_otg *x) +{ + unsigned long flags; + struct dwc3_otg *otg; + + if (!x) + return -ENODEV; + + otg = otg_to_dwc3_otg(x); + otg_dbg(otg, "\n"); + + if (!otg->otg.host || !otg->otg.gadget) + return -ENODEV; + + spin_lock_irqsave(&otg->lock, flags); + otg->user_events |= USER_HNP_EVENT; + wakeup_main_thread(otg); + spin_unlock_irqrestore(&otg->lock, flags); + return 0; +} + +static int dwc3_otg_end_session(struct usb_otg *x) +{ + unsigned long flags; + struct dwc3_otg *otg; + + if (!x) + return -ENODEV; + + otg = otg_to_dwc3_otg(x); + otg_dbg(otg, "\n"); + + if (!otg->otg.host || !otg->otg.gadget) + return -ENODEV; + + spin_lock_irqsave(&otg->lock, flags); + otg->user_events |= USER_END_SESSION; + wakeup_main_thread(otg); + spin_unlock_irqrestore(&otg->lock, flags); + return 0; +} + +static int otg_end_session(struct usb_otg *otg) +{ + return dwc3_otg_end_session(otg); +} +EXPORT_SYMBOL(otg_end_session); + +static int dwc3_otg_received_host_release(struct usb_otg *x) +{ + struct dwc3_otg *otg; + unsigned long flags; + + if (!x) + return -ENODEV; + + otg = otg_to_dwc3_otg(x); + otg_dbg(otg, "\n"); + + if (!otg->otg.host || !otg->otg.gadget) + return -ENODEV; + + spin_lock_irqsave(&otg->lock, flags); + otg->user_events |= PCD_RECEIVED_HOST_RELEASE_EVENT; + wakeup_main_thread(otg); + spin_unlock_irqrestore(&otg->lock, flags); + return 0; +} + +int otg_host_release(struct usb_otg *otg) +{ + return dwc3_otg_received_host_release(otg); +} +EXPORT_SYMBOL(otg_host_release); + +static void dwc3_otg_enable_irq(struct dwc3_otg *otg) +{ + u32 reg; + + /* Enable OTG IRQs */ + reg = OEVT_ALL; + + otg_write(otg, OEVTEN, reg); +} + +static ssize_t store_srp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_phy *phy; + struct usb_otg *otg; + + phy = usb_get_phy(USB_PHY_TYPE_USB3); + if (IS_ERR(phy) || !phy) { + if (!IS_ERR(phy)) + usb_put_phy(phy); + return count; + } + + otg = phy->otg; + if (!otg) { + usb_put_phy(phy); + return count; + } + + otg_start_srp(otg); + usb_put_phy(phy); + return count; +} +static DEVICE_ATTR(srp, 0220, NULL, store_srp); + +static ssize_t store_end(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_phy *phy; + struct usb_otg *otg; + + phy = usb_get_phy(USB_PHY_TYPE_USB3); + if (IS_ERR(phy) || !phy) { + if (!IS_ERR(phy)) + usb_put_phy(phy); + return count; + } + + otg = phy->otg; + if (!otg) { + usb_put_phy(phy); + return count; + } + + otg_end_session(otg); + usb_put_phy(phy); + return count; +} +static DEVICE_ATTR(end, 0220, NULL, store_end); + +static ssize_t store_hnp(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct usb_phy *phy = usb_get_phy(USB_PHY_TYPE_USB3); + struct usb_otg *otg; + + dev_dbg(dwc->dev, "%s()\n", __func__); + + if (IS_ERR(phy) || !phy) { + dev_info(dwc->dev, "NO PHY!!\n"); + if (!IS_ERR(phy)) + usb_put_phy(phy); + return count; + } + + otg = phy->otg; + if (!otg) { + dev_info(dwc->dev, "NO OTG!!\n"); + usb_put_phy(phy); + return count; + } + + dev_info(dev, "b_hnp_enable is FALSE\n"); + dwc->gadget.host_request_flag = 1; + + usb_put_phy(phy); + return count; +} +static DEVICE_ATTR(hnp, 0220, NULL, store_hnp); + +static ssize_t store_a_hnp_reqd(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_otg *otg; + + otg = dwc->otg; + host_release(otg); + return count; +} +static DEVICE_ATTR(a_hnp_reqd, 0220, NULL, store_a_hnp_reqd); + +static ssize_t store_print_dbg(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + struct dwc3_otg *otg; + + otg = dwc->otg; + print_debug_regs(otg); + + return count; +} +static DEVICE_ATTR(print_dbg, 0220, NULL, store_print_dbg); + +void dwc_usb3_remove_dev_files(struct device *dev) +{ + device_remove_file(dev, &dev_attr_print_dbg); + device_remove_file(dev, &dev_attr_a_hnp_reqd); + device_remove_file(dev, &dev_attr_end); + device_remove_file(dev, &dev_attr_srp); + device_remove_file(dev, &dev_attr_hnp); +} + +int dwc3_otg_create_dev_files(struct device *dev) +{ + int retval; + + retval = device_create_file(dev, &dev_attr_hnp); + if (retval) + goto fail; + + retval = device_create_file(dev, &dev_attr_srp); + if (retval) + goto fail; + + retval = device_create_file(dev, &dev_attr_end); + if (retval) + goto fail; + + retval = device_create_file(dev, &dev_attr_a_hnp_reqd); + if (retval) + goto fail; + + retval = device_create_file(dev, &dev_attr_print_dbg); + if (retval) + goto fail; + + return 0; + +fail: + dev_err(dev, "Failed to create one or more sysfs files!!\n"); + return retval; +} + +int dwc3_otg_init(struct dwc3 *dwc) +{ + struct dwc3_otg *otg; + int err; + u32 reg; + + dev_dbg(dwc->dev, "dwc3_otg_init\n"); + + /* + * GHWPARAMS6[10] bit is SRPSupport. + * This bit also reflects DWC_USB3_EN_OTG + */ + reg = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6); + if (!(reg & GHWPARAMS6_SRP_SUPPORT_ENABLED)) { + /* + * No OTG support in the HW core. + * We return 0 to indicate no error, since this is acceptable + * situation, just continue probe the dwc3 driver without otg. + */ + dev_dbg(dwc->dev, "dwc3_otg address space is not supported\n"); + return 0; + } + + otg = kzalloc(sizeof(*otg), GFP_KERNEL); + if (!otg) + return -ENOMEM; + + dwc->otg = otg; + otg->dev = dwc->dev; + otg->dwc = dwc; + + otg->regs = dwc->regs - DWC3_GLOBALS_REGS_START; + otg->otg.usb_phy = kzalloc(sizeof(struct usb_phy), GFP_KERNEL); + otg->otg.usb_phy->dev = otg->dev; + otg->otg.usb_phy->label = "dwc3_otg"; + otg->otg.state = OTG_STATE_UNDEFINED; + otg->otg.usb_phy->otg = &otg->otg; + otg->otg.usb_phy->notify_connect = dwc3_otg_notify_connect; + otg->otg.usb_phy->notify_disconnect = dwc3_otg_notify_disconnect; + + otg->otg.start_srp = dwc3_otg_start_srp; + otg->otg.start_hnp = dwc3_otg_start_hnp; + otg->otg.set_host = dwc3_otg_set_host; + otg->otg.set_peripheral = dwc3_otg_set_periph; + + otg->hwparams6 = reg; + otg->state = OTG_STATE_UNDEFINED; + + spin_lock_init(&otg->lock); + init_waitqueue_head(&otg->main_wq); + + err = usb_add_phy(otg->otg.usb_phy, USB_PHY_TYPE_USB3); + if (err) { + dev_err(otg->dev, "can't register transceiver, err: %d\n", + err); + goto exit; + } + + otg->irq = platform_get_irq(to_platform_device(otg->dev), 1); + + dwc3_otg_create_dev_files(otg->dev); + + /* Set irq handler */ + err = request_irq(otg->irq, dwc3_otg_irq, IRQF_SHARED, "dwc3_otg", otg); + if (err) { + dev_err(otg->otg.usb_phy->dev, "failed to request irq #%d --> %d\n", + otg->irq, err); + goto exit; + } + + dwc3_otg_enable_irq(otg); + + return 0; +exit: + kfree(otg->otg.usb_phy); + kfree(otg); + return err; +} + +void dwc3_otg_exit(struct dwc3 *dwc) +{ + struct dwc3_otg *otg = dwc->otg; + + otg_dbg(otg, "\n"); + usb_remove_phy(otg->otg.usb_phy); + kfree(otg->otg.usb_phy); + kfree(otg); +} diff --git a/drivers/usb/dwc3/otg.h b/drivers/usb/dwc3/otg.h new file mode 100644 index 0000000..e2eb4ca --- /dev/null +++ b/drivers/usb/dwc3/otg.h @@ -0,0 +1,247 @@ +/** + * otg.h - DesignWare USB3 DRD OTG Header + * + * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com + * + * Authors: Felipe Balbi , + * Sebastian Andrzej Siewior + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define otg_dbg(d, fmt, args...) dev_dbg((d)->dev, "%s(): " fmt,\ + __func__, ## args) +#define otg_vdbg(d, fmt, args...) dev_vdbg((d)->dev, "%s(): " fmt,\ + __func__, ## args) +#define otg_err(d, fmt, args...) dev_err((d)->dev, "%s(): ERROR: " fmt,\ + __func__, ## args) +#define otg_warn(d, fmt, args...) dev_warn((d)->dev, "%s(): WARN: " fmt,\ + __func__, ## args) +#define otg_info(d, fmt, args...) dev_info((d)->dev, "%s(): INFO: " fmt,\ + __func__, ## args) + +#ifdef VERBOSE_DEBUG +#define otg_write(o, reg, val) do { \ + otg_vdbg(o, "OTG_WRITE: reg=0x%05x, val=0x%08x\n", reg, val); \ + writel(val, ((void *)((o)->regs)) + reg); \ + } while (0) + +#define otg_read(o, reg) ({ \ + u32 __r = readl(((void *)((o)->regs)) + reg); \ + otg_vdbg(o, "OTG_READ: reg=0x%05x, val=0x%08x\n", reg, __r); \ + __r; \ + }) +#else +#define otg_write(o, reg, val) writel(val, ((void *)((o)->regs)) + reg) +#define otg_read(o, reg) readl(((void *)((o)->regs)) + reg) +#endif + +#define sleep_main_thread_until_condition_timeout(otg, condition, msecs) ({ \ + int __timeout = msecs; \ + while (!(condition)) { \ + otg_dbg(otg, " ... sleeping for %d\n", __timeout); \ + __timeout = sleep_main_thread_timeout(otg, __timeout); \ + if (__timeout <= 0) { \ + break; \ + } \ + } \ + __timeout; \ + }) + +#define sleep_main_thread_until_condition(otg, condition) ({ \ + int __rc; \ + do { \ + __rc = sleep_main_thread_until_condition_timeout(otg, \ + condition, 50000); \ + } while (__rc == 0); \ + __rc; \ + }) + +#define GHWPARAMS6 0xc158 +#define GHWPARAMS6_SRP_SUPPORT_ENABLED 0x0400 +#define GHWPARAMS6_HNP_SUPPORT_ENABLED 0x0800 + +#define GCTL 0xc110 +#define GCTL_PRT_CAP_DIR 0x3000 +#define GCTL_PRT_CAP_DIR_SHIFT 12 +#define GCTL_PRT_CAP_DIR_HOST 1 +#define GCTL_PRT_CAP_DIR_DEV 2 +#define GCTL_PRT_CAP_DIR_OTG 3 +#define GCTL_GBL_HIBERNATION_EN 0x2 + +#define OCFG 0xcc00 +#define OCFG_SRP_CAP 0x01 +#define OCFG_SRP_CAP_SHIFT 0 +#define OCFG_HNP_CAP 0x02 +#define OCFG_HNP_CAP_SHIFT 1 +#define OCFG_OTG_VERSION 0x04 +#define OCFG_OTG_VERSION_SHIFT 2 + +#define OCTL 0xcc04 +#define OCTL_HST_SET_HNP_EN 0x01 +#define OCTL_HST_SET_HNP_EN_SHIFT 0 +#define OCTL_DEV_SET_HNP_EN 0x02 +#define OCTL_DEV_SET_HNP_EN_SHIFT 1 +#define OCTL_TERM_SEL_DL_PULSE 0x04 +#define OCTL_TERM_SEL_DL_PULSE_SHIFT 2 +#define OCTL_SES_REQ 0x08 +#define OCTL_SES_REQ_SHIFT 3 +#define OCTL_HNP_REQ 0x10 +#define OCTL_HNP_REQ_SHIFT 4 +#define OCTL_PRT_PWR_CTL 0x20 +#define OCTL_PRT_PWR_CTL_SHIFT 5 +#define OCTL_PERI_MODE 0x40 +#define OCTL_PERI_MODE_SHIFT 6 + +#define OEVT 0xcc08 +#define OEVT_ERR 0x00000001 +#define OEVT_ERR_SHIFT 0 +#define OEVT_SES_REQ_SCS 0x00000002 +#define OEVT_SES_REQ_SCS_SHIFT 1 +#define OEVT_HST_NEG_SCS 0x00000004 +#define OEVT_HST_NEG_SCS_SHIFT 2 +#define OEVT_B_SES_VLD_EVT 0x00000008 +#define OEVT_B_SES_VLD_EVT_SHIFT 3 +#define OEVT_B_DEV_VBUS_CHNG_EVNT 0x00000100 +#define OEVT_B_DEV_VBUS_CHNG_EVNT_SHIFT 8 +#define OEVT_B_DEV_SES_VLD_DET_EVNT 0x00000200 +#define OEVT_B_DEV_SES_VLD_DET_EVNT_SHIFT 9 +#define OEVT_B_DEV_HNP_CHNG_EVNT 0x00000400 +#define OEVT_B_DEV_HNP_CHNG_EVNT_SHIFT 10 +#define OEVT_B_DEV_B_HOST_END_EVNT 0x00000800 +#define OEVT_B_DEV_B_HOST_END_EVNT_SHIFT 11 +#define OEVT_A_DEV_SESS_END_DET_EVNT 0x00010000 +#define OEVT_A_DEV_SESS_END_DET_EVNT_SHIFT 16 +#define OEVT_A_DEV_SRP_DET_EVNT 0x00020000 +#define OEVT_A_DEV_SRP_DET_EVNT_SHIFT 17 +#define OEVT_A_DEV_HNP_CHNG_EVNT 0x00040000 +#define OEVT_A_DEV_HNP_CHNG_EVNT_SHIFT 18 +#define OEVT_A_DEV_HOST_EVNT 0x00080000 +#define OEVT_A_DEV_HOST_EVNT_SHIFT 19 +#define OEVT_A_DEV_B_DEV_HOST_END_EVNT 0x00100000 +#define OEVT_A_DEV_B_DEV_HOST_END_EVNT_SHIFT 20 +#define OEVT_A_DEV_IDLE_EVNT 0x00200000 +#define OEVT_A_DEV_IDLE_EVNT_SHIFT 21 +#define OEVT_HOST_ROLE_REQ_INIT_EVNT 0x00400000 +#define OEVT_HOST_ROLE_REQ_INIT_EVNT_SHIFT 22 +#define OEVT_HOST_ROLE_REQ_CONFIRM_EVNT 0x00800000 +#define OEVT_HOST_ROLE_REQ_CONFIRM_EVNT_SHIFT 23 +#define OEVT_CONN_ID_STS_CHNG_EVNT 0x01000000 +#define OEVT_CONN_ID_STS_CHNG_EVNT_SHIFT 24 +#define OEVT_DEV_MOD_EVNT 0x80000000 +#define OEVT_DEV_MOD_EVNT_SHIFT 31 + +#define OEVTEN 0xcc0c + +#define OEVT_ALL (OEVT_CONN_ID_STS_CHNG_EVNT | \ + OEVT_HOST_ROLE_REQ_INIT_EVNT | \ + OEVT_HOST_ROLE_REQ_CONFIRM_EVNT | \ + OEVT_A_DEV_B_DEV_HOST_END_EVNT | \ + OEVT_A_DEV_HOST_EVNT | \ + OEVT_A_DEV_HNP_CHNG_EVNT | \ + OEVT_A_DEV_SRP_DET_EVNT | \ + OEVT_A_DEV_SESS_END_DET_EVNT | \ + OEVT_B_DEV_B_HOST_END_EVNT | \ + OEVT_B_DEV_HNP_CHNG_EVNT | \ + OEVT_B_DEV_SES_VLD_DET_EVNT | \ + OEVT_B_DEV_VBUS_CHNG_EVNT) + +#define OSTS 0xcc10 +#define OSTS_CONN_ID_STS 0x0001 +#define OSTS_CONN_ID_STS_SHIFT 0 +#define OSTS_A_SES_VLD 0x0002 +#define OSTS_A_SES_VLD_SHIFT 1 +#define OSTS_B_SES_VLD 0x0004 +#define OSTS_B_SES_VLD_SHIFT 2 +#define OSTS_XHCI_PRT_PWR 0x0008 +#define OSTS_XHCI_PRT_PWR_SHIFT 3 +#define OSTS_PERIP_MODE 0x0010 +#define OSTS_PERIP_MODE_SHIFT 4 +#define OSTS_OTG_STATES 0x0f00 +#define OSTS_OTG_STATE_SHIFT 8 + +#define DCTL 0xc704 +#define DCTL_RUN_STOP 0x80000000 + +#define OTG_STATE_INVALID -1 +#define OTG_STATE_EXIT 14 +#define OTG_STATE_TERMINATED 15 + +#define PERI_MODE_HOST 0 +#define PERI_MODE_PERIPHERAL 1 + +/** The main structure to keep track of OTG driver state. */ +struct dwc3_otg { + + /** OTG PHY */ + struct usb_otg otg; + struct device *dev; + struct dwc3 *dwc; + + void __iomem *regs; + + int main_wakeup_needed; + struct task_struct *main_thread; + wait_queue_head_t main_wq; + + spinlock_t lock; + + int otg_srp_reqd; + + /* Events */ + u32 otg_events; + + u32 user_events; + + /** User initiated SRP. + * + * Valid in B-device during sensing/probing. Initiates SRP signalling + * across the bus. + * + * Also valid as an A-device during probing. This causes the A-device to + * apply V-bus manually and check for a device. Can be used if the + * device does not support SRP and the host does not support ADP. + */ +#define USER_SRP_EVENT 0x1 + /** User initiated HNP (only valid in B-peripheral) */ +#define USER_HNP_EVENT 0x2 + /** User has ended the session (only valid in B-peripheral) */ +#define USER_END_SESSION 0x4 + /** User initiated VBUS. This will cause the A-device to turn on the + * VBUS and see if a device will connect (only valid in A-device during + * sensing/probing) + */ +#define USER_VBUS_ON 0x8 + /** User has initiated RSP */ +#define USER_RSP_EVENT 0x10 + /** Host release event */ +#define PCD_RECEIVED_HOST_RELEASE_EVENT 0x20 + /** Initial SRP */ +#define INITIAL_SRP 0x40 + /** A-device connected event*/ +#define USER_A_CONN_EVENT 0x80 + + /* States */ + enum usb_otg_state prev; + enum usb_otg_state state; + + u32 hwparams6; + int hcd_irq; + int irq; + int host_started; + int peripheral_started; + int dev_enum; + + struct delayed_work hp_work; /* drives HNP polling */ + +}; + +extern int usb_port_suspend(struct usb_device *udev, pm_message_t msg); +extern void usb_kick_hub_wq(struct usb_device *dev);