18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * drd.c - DesignWare USB3 DRD Controller Dual-role support 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: Roger Quadros <rogerq@ti.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/extcon.h> 118c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/property.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "debug.h" 168c2ecf20Sopenharmony_ci#include "core.h" 178c2ecf20Sopenharmony_ci#include "gadget.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic void dwc3_otg_disable_events(struct dwc3 *dwc, u32 disable_mask) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN); 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci reg &= ~(disable_mask); 248c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_OEVTEN, reg); 258c2ecf20Sopenharmony_ci} 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic void dwc3_otg_enable_events(struct dwc3 *dwc, u32 enable_mask) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci u32 reg = dwc3_readl(dwc->regs, DWC3_OEVTEN); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci reg |= (enable_mask); 328c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_OEVTEN, reg); 338c2ecf20Sopenharmony_ci} 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic void dwc3_otg_clear_events(struct dwc3 *dwc) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci u32 reg = dwc3_readl(dwc->regs, DWC3_OEVT); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_OEVTEN, reg); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define DWC3_OTG_ALL_EVENTS (DWC3_OEVTEN_XHCIRUNSTPSETEN | \ 438c2ecf20Sopenharmony_ci DWC3_OEVTEN_DEVRUNSTPSETEN | DWC3_OEVTEN_HIBENTRYEN | \ 448c2ecf20Sopenharmony_ci DWC3_OEVTEN_CONIDSTSCHNGEN | DWC3_OEVTEN_HRRCONFNOTIFEN | \ 458c2ecf20Sopenharmony_ci DWC3_OEVTEN_HRRINITNOTIFEN | DWC3_OEVTEN_ADEVIDLEEN | \ 468c2ecf20Sopenharmony_ci DWC3_OEVTEN_ADEVBHOSTENDEN | DWC3_OEVTEN_ADEVHOSTEN | \ 478c2ecf20Sopenharmony_ci DWC3_OEVTEN_ADEVHNPCHNGEN | DWC3_OEVTEN_ADEVSRPDETEN | \ 488c2ecf20Sopenharmony_ci DWC3_OEVTEN_ADEVSESSENDDETEN | DWC3_OEVTEN_BDEVBHOSTENDEN | \ 498c2ecf20Sopenharmony_ci DWC3_OEVTEN_BDEVHNPCHNGEN | DWC3_OEVTEN_BDEVSESSVLDDETEN | \ 508c2ecf20Sopenharmony_ci DWC3_OEVTEN_BDEVVBUSCHNGEN) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic irqreturn_t dwc3_otg_thread_irq(int irq, void *_dwc) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct dwc3 *dwc = _dwc; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci spin_lock(&dwc->lock); 578c2ecf20Sopenharmony_ci if (dwc->otg_restart_host) { 588c2ecf20Sopenharmony_ci dwc3_otg_host_init(dwc); 598c2ecf20Sopenharmony_ci dwc->otg_restart_host = false; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci spin_unlock(&dwc->lock); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return IRQ_HANDLED; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic irqreturn_t dwc3_otg_irq(int irq, void *_dwc) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci u32 reg; 728c2ecf20Sopenharmony_ci struct dwc3 *dwc = _dwc; 738c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci reg = dwc3_readl(dwc->regs, DWC3_OEVT); 768c2ecf20Sopenharmony_ci if (reg) { 778c2ecf20Sopenharmony_ci /* ignore non OTG events, we can't disable them in OEVTEN */ 788c2ecf20Sopenharmony_ci if (!(reg & DWC3_OTG_ALL_EVENTS)) { 798c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_OEVT, reg); 808c2ecf20Sopenharmony_ci return IRQ_NONE; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST && 848c2ecf20Sopenharmony_ci !(reg & DWC3_OEVT_DEVICEMODE)) 858c2ecf20Sopenharmony_ci dwc->otg_restart_host = true; 868c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_OEVT, reg); 878c2ecf20Sopenharmony_ci ret = IRQ_WAKE_THREAD; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return ret; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic void dwc3_otgregs_init(struct dwc3 *dwc) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci u32 reg; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* 988c2ecf20Sopenharmony_ci * Prevent host/device reset from resetting OTG core. 998c2ecf20Sopenharmony_ci * If we don't do this then xhci_reset (USBCMD.HCRST) will reset 1008c2ecf20Sopenharmony_ci * the signal outputs sent to the PHY, the OTG FSM logic of the 1018c2ecf20Sopenharmony_ci * core and also the resets to the VBUS filters inside the core. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci reg = dwc3_readl(dwc->regs, DWC3_OCFG); 1048c2ecf20Sopenharmony_ci reg |= DWC3_OCFG_SFTRSTMASK; 1058c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_OCFG, reg); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* Disable hibernation for simplicity */ 1088c2ecf20Sopenharmony_ci reg = dwc3_readl(dwc->regs, DWC3_GCTL); 1098c2ecf20Sopenharmony_ci reg &= ~DWC3_GCTL_GBLHIBERNATIONEN; 1108c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_GCTL, reg); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci /* 1138c2ecf20Sopenharmony_ci * Initialize OTG registers as per 1148c2ecf20Sopenharmony_ci * Figure 11-4 OTG Driver Overall Programming Flow 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_ci /* OCFG.SRPCap = 0, OCFG.HNPCap = 0 */ 1178c2ecf20Sopenharmony_ci reg = dwc3_readl(dwc->regs, DWC3_OCFG); 1188c2ecf20Sopenharmony_ci reg &= ~(DWC3_OCFG_SRPCAP | DWC3_OCFG_HNPCAP); 1198c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_OCFG, reg); 1208c2ecf20Sopenharmony_ci /* OEVT = FFFF */ 1218c2ecf20Sopenharmony_ci dwc3_otg_clear_events(dwc); 1228c2ecf20Sopenharmony_ci /* OEVTEN = 0 */ 1238c2ecf20Sopenharmony_ci dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS); 1248c2ecf20Sopenharmony_ci /* OEVTEN.ConIDStsChngEn = 1. Instead we enable all events */ 1258c2ecf20Sopenharmony_ci dwc3_otg_enable_events(dwc, DWC3_OTG_ALL_EVENTS); 1268c2ecf20Sopenharmony_ci /* 1278c2ecf20Sopenharmony_ci * OCTL.PeriMode = 1, OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0, 1288c2ecf20Sopenharmony_ci * OCTL.HNPReq = 0 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_ci reg = dwc3_readl(dwc->regs, DWC3_OCTL); 1318c2ecf20Sopenharmony_ci reg |= DWC3_OCTL_PERIMODE; 1328c2ecf20Sopenharmony_ci reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN | 1338c2ecf20Sopenharmony_ci DWC3_OCTL_HNPREQ); 1348c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_OCTL, reg); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int dwc3_otg_get_irq(struct dwc3 *dwc) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct platform_device *dwc3_pdev = to_platform_device(dwc->dev); 1408c2ecf20Sopenharmony_ci int irq; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci irq = platform_get_irq_byname_optional(dwc3_pdev, "otg"); 1438c2ecf20Sopenharmony_ci if (irq > 0) 1448c2ecf20Sopenharmony_ci goto out; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (irq == -EPROBE_DEFER) 1478c2ecf20Sopenharmony_ci goto out; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci irq = platform_get_irq_byname_optional(dwc3_pdev, "dwc_usb3"); 1508c2ecf20Sopenharmony_ci if (irq > 0) 1518c2ecf20Sopenharmony_ci goto out; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (irq == -EPROBE_DEFER) 1548c2ecf20Sopenharmony_ci goto out; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci irq = platform_get_irq(dwc3_pdev, 0); 1578c2ecf20Sopenharmony_ci if (irq > 0) 1588c2ecf20Sopenharmony_ci goto out; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (!irq) 1618c2ecf20Sopenharmony_ci irq = -EINVAL; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ciout: 1648c2ecf20Sopenharmony_ci return irq; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_civoid dwc3_otg_init(struct dwc3 *dwc) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci u32 reg; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* 1728c2ecf20Sopenharmony_ci * As per Figure 11-4 OTG Driver Overall Programming Flow, 1738c2ecf20Sopenharmony_ci * block "Initialize GCTL for OTG operation". 1748c2ecf20Sopenharmony_ci */ 1758c2ecf20Sopenharmony_ci /* GCTL.PrtCapDir=2'b11 */ 1768c2ecf20Sopenharmony_ci dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG); 1778c2ecf20Sopenharmony_ci /* GUSB2PHYCFG0.SusPHY=0 */ 1788c2ecf20Sopenharmony_ci reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); 1798c2ecf20Sopenharmony_ci reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; 1808c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* Initialize OTG registers */ 1838c2ecf20Sopenharmony_ci dwc3_otgregs_init(dwc); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_civoid dwc3_otg_exit(struct dwc3 *dwc) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci /* disable all OTG IRQs */ 1898c2ecf20Sopenharmony_ci dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS); 1908c2ecf20Sopenharmony_ci /* clear all events */ 1918c2ecf20Sopenharmony_ci dwc3_otg_clear_events(dwc); 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci/* should be called before Host controller driver is started */ 1958c2ecf20Sopenharmony_civoid dwc3_otg_host_init(struct dwc3 *dwc) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci u32 reg; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* As per Figure 11-10 A-Device Flow Diagram */ 2008c2ecf20Sopenharmony_ci /* OCFG.HNPCap = 0, OCFG.SRPCap = 0. Already 0 */ 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* 2038c2ecf20Sopenharmony_ci * OCTL.PeriMode=0, OCTL.TermSelDLPulse = 0, 2048c2ecf20Sopenharmony_ci * OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci reg = dwc3_readl(dwc->regs, DWC3_OCTL); 2078c2ecf20Sopenharmony_ci reg &= ~(DWC3_OCTL_PERIMODE | DWC3_OCTL_TERMSELIDPULSE | 2088c2ecf20Sopenharmony_ci DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN); 2098c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_OCTL, reg); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci /* 2128c2ecf20Sopenharmony_ci * OCFG.DisPrtPwrCutoff = 0/1 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci reg = dwc3_readl(dwc->regs, DWC3_OCFG); 2158c2ecf20Sopenharmony_ci reg &= ~DWC3_OCFG_DISPWRCUTTOFF; 2168c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_OCFG, reg); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* 2198c2ecf20Sopenharmony_ci * OCFG.SRPCap = 1, OCFG.HNPCap = GHWPARAMS6.HNP_CAP 2208c2ecf20Sopenharmony_ci * We don't want SRP/HNP for simple dual-role so leave 2218c2ecf20Sopenharmony_ci * these disabled. 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* 2258c2ecf20Sopenharmony_ci * OEVTEN.OTGADevHostEvntEn = 1 2268c2ecf20Sopenharmony_ci * OEVTEN.OTGADevSessEndDetEvntEn = 1 2278c2ecf20Sopenharmony_ci * We don't want HNP/role-swap so leave these disabled. 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* GUSB2PHYCFG.ULPIAutoRes = 1/0, GUSB2PHYCFG.SusPHY = 1 */ 2318c2ecf20Sopenharmony_ci if (!dwc->dis_u2_susphy_quirk) { 2328c2ecf20Sopenharmony_ci reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); 2338c2ecf20Sopenharmony_ci reg |= DWC3_GUSB2PHYCFG_SUSPHY; 2348c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* Set Port Power to enable VBUS: OCTL.PrtPwrCtl = 1 */ 2388c2ecf20Sopenharmony_ci reg = dwc3_readl(dwc->regs, DWC3_OCTL); 2398c2ecf20Sopenharmony_ci reg |= DWC3_OCTL_PRTPWRCTL; 2408c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_OCTL, reg); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/* should be called after Host controller driver is stopped */ 2448c2ecf20Sopenharmony_cistatic void dwc3_otg_host_exit(struct dwc3 *dwc) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci u32 reg; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* 2498c2ecf20Sopenharmony_ci * Exit from A-device flow as per 2508c2ecf20Sopenharmony_ci * Figure 11-4 OTG Driver Overall Programming Flow 2518c2ecf20Sopenharmony_ci */ 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* 2548c2ecf20Sopenharmony_ci * OEVTEN.OTGADevBHostEndEvntEn=0, OEVTEN.OTGADevHNPChngEvntEn=0 2558c2ecf20Sopenharmony_ci * OEVTEN.OTGADevSessEndDetEvntEn=0, 2568c2ecf20Sopenharmony_ci * OEVTEN.OTGADevHostEvntEn = 0 2578c2ecf20Sopenharmony_ci * But we don't disable any OTG events 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* OCTL.HstSetHNPEn = 0, OCTL.PrtPwrCtl=0 */ 2618c2ecf20Sopenharmony_ci reg = dwc3_readl(dwc->regs, DWC3_OCTL); 2628c2ecf20Sopenharmony_ci reg &= ~(DWC3_OCTL_HSTSETHNPEN | DWC3_OCTL_PRTPWRCTL); 2638c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_OCTL, reg); 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci/* should be called before the gadget controller driver is started */ 2678c2ecf20Sopenharmony_cistatic void dwc3_otg_device_init(struct dwc3 *dwc) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci u32 reg; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* As per Figure 11-20 B-Device Flow Diagram */ 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* 2748c2ecf20Sopenharmony_ci * OCFG.HNPCap = GHWPARAMS6.HNP_CAP, OCFG.SRPCap = 1 2758c2ecf20Sopenharmony_ci * but we keep them 0 for simple dual-role operation. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ci reg = dwc3_readl(dwc->regs, DWC3_OCFG); 2788c2ecf20Sopenharmony_ci /* OCFG.OTGSftRstMsk = 0/1 */ 2798c2ecf20Sopenharmony_ci reg |= DWC3_OCFG_SFTRSTMASK; 2808c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_OCFG, reg); 2818c2ecf20Sopenharmony_ci /* 2828c2ecf20Sopenharmony_ci * OCTL.PeriMode = 1 2838c2ecf20Sopenharmony_ci * OCTL.TermSelDLPulse = 0/1, OCTL.HNPReq = 0 2848c2ecf20Sopenharmony_ci * OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_ci reg = dwc3_readl(dwc->regs, DWC3_OCTL); 2878c2ecf20Sopenharmony_ci reg |= DWC3_OCTL_PERIMODE; 2888c2ecf20Sopenharmony_ci reg &= ~(DWC3_OCTL_TERMSELIDPULSE | DWC3_OCTL_HNPREQ | 2898c2ecf20Sopenharmony_ci DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN); 2908c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_OCTL, reg); 2918c2ecf20Sopenharmony_ci /* OEVTEN.OTGBDevSesVldDetEvntEn = 1 */ 2928c2ecf20Sopenharmony_ci dwc3_otg_enable_events(dwc, DWC3_OEVTEN_BDEVSESSVLDDETEN); 2938c2ecf20Sopenharmony_ci /* GUSB2PHYCFG.ULPIAutoRes = 0, GUSB2PHYCFG0.SusPHY = 1 */ 2948c2ecf20Sopenharmony_ci if (!dwc->dis_u2_susphy_quirk) { 2958c2ecf20Sopenharmony_ci reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); 2968c2ecf20Sopenharmony_ci reg |= DWC3_GUSB2PHYCFG_SUSPHY; 2978c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci /* GCTL.GblHibernationEn = 0. Already 0. */ 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci/* should be called after the gadget controller driver is stopped */ 3038c2ecf20Sopenharmony_cistatic void dwc3_otg_device_exit(struct dwc3 *dwc) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci u32 reg; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci /* 3088c2ecf20Sopenharmony_ci * Exit from B-device flow as per 3098c2ecf20Sopenharmony_ci * Figure 11-4 OTG Driver Overall Programming Flow 3108c2ecf20Sopenharmony_ci */ 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* 3138c2ecf20Sopenharmony_ci * OEVTEN.OTGBDevHNPChngEvntEn = 0 3148c2ecf20Sopenharmony_ci * OEVTEN.OTGBDevVBusChngEvntEn = 0 3158c2ecf20Sopenharmony_ci * OEVTEN.OTGBDevBHostEndEvntEn = 0 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_ci dwc3_otg_disable_events(dwc, DWC3_OEVTEN_BDEVHNPCHNGEN | 3188c2ecf20Sopenharmony_ci DWC3_OEVTEN_BDEVVBUSCHNGEN | 3198c2ecf20Sopenharmony_ci DWC3_OEVTEN_BDEVBHOSTENDEN); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* OCTL.DevSetHNPEn = 0, OCTL.HNPReq = 0, OCTL.PeriMode=1 */ 3228c2ecf20Sopenharmony_ci reg = dwc3_readl(dwc->regs, DWC3_OCTL); 3238c2ecf20Sopenharmony_ci reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HNPREQ); 3248c2ecf20Sopenharmony_ci reg |= DWC3_OCTL_PERIMODE; 3258c2ecf20Sopenharmony_ci dwc3_writel(dwc->regs, DWC3_OCTL, reg); 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_civoid dwc3_otg_update(struct dwc3 *dwc, bool ignore_idstatus) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci int ret; 3318c2ecf20Sopenharmony_ci u32 reg; 3328c2ecf20Sopenharmony_ci int id; 3338c2ecf20Sopenharmony_ci unsigned long flags; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (dwc->dr_mode != USB_DR_MODE_OTG) 3368c2ecf20Sopenharmony_ci return; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* don't do anything if debug user changed role to not OTG */ 3398c2ecf20Sopenharmony_ci if (dwc->current_dr_role != DWC3_GCTL_PRTCAP_OTG) 3408c2ecf20Sopenharmony_ci return; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (!ignore_idstatus) { 3438c2ecf20Sopenharmony_ci reg = dwc3_readl(dwc->regs, DWC3_OSTS); 3448c2ecf20Sopenharmony_ci id = !!(reg & DWC3_OSTS_CONIDSTS); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci dwc->desired_otg_role = id ? DWC3_OTG_ROLE_DEVICE : 3478c2ecf20Sopenharmony_ci DWC3_OTG_ROLE_HOST; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (dwc->desired_otg_role == dwc->current_otg_role) 3518c2ecf20Sopenharmony_ci return; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci switch (dwc->current_otg_role) { 3548c2ecf20Sopenharmony_ci case DWC3_OTG_ROLE_HOST: 3558c2ecf20Sopenharmony_ci dwc3_host_exit(dwc); 3568c2ecf20Sopenharmony_ci spin_lock_irqsave(&dwc->lock, flags); 3578c2ecf20Sopenharmony_ci dwc3_otg_host_exit(dwc); 3588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dwc->lock, flags); 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci case DWC3_OTG_ROLE_DEVICE: 3618c2ecf20Sopenharmony_ci dwc3_gadget_exit(dwc); 3628c2ecf20Sopenharmony_ci spin_lock_irqsave(&dwc->lock, flags); 3638c2ecf20Sopenharmony_ci dwc3_event_buffers_cleanup(dwc); 3648c2ecf20Sopenharmony_ci dwc3_otg_device_exit(dwc); 3658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dwc->lock, flags); 3668c2ecf20Sopenharmony_ci break; 3678c2ecf20Sopenharmony_ci default: 3688c2ecf20Sopenharmony_ci break; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci spin_lock_irqsave(&dwc->lock, flags); 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci dwc->current_otg_role = dwc->desired_otg_role; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dwc->lock, flags); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci switch (dwc->desired_otg_role) { 3788c2ecf20Sopenharmony_ci case DWC3_OTG_ROLE_HOST: 3798c2ecf20Sopenharmony_ci spin_lock_irqsave(&dwc->lock, flags); 3808c2ecf20Sopenharmony_ci dwc3_otgregs_init(dwc); 3818c2ecf20Sopenharmony_ci dwc3_otg_host_init(dwc); 3828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dwc->lock, flags); 3838c2ecf20Sopenharmony_ci ret = dwc3_host_init(dwc); 3848c2ecf20Sopenharmony_ci if (ret) { 3858c2ecf20Sopenharmony_ci dev_err(dwc->dev, "failed to initialize host\n"); 3868c2ecf20Sopenharmony_ci } else { 3878c2ecf20Sopenharmony_ci if (dwc->usb2_phy) 3888c2ecf20Sopenharmony_ci otg_set_vbus(dwc->usb2_phy->otg, true); 3898c2ecf20Sopenharmony_ci if (dwc->usb2_generic_phy) 3908c2ecf20Sopenharmony_ci phy_set_mode(dwc->usb2_generic_phy, 3918c2ecf20Sopenharmony_ci PHY_MODE_USB_HOST); 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci break; 3948c2ecf20Sopenharmony_ci case DWC3_OTG_ROLE_DEVICE: 3958c2ecf20Sopenharmony_ci spin_lock_irqsave(&dwc->lock, flags); 3968c2ecf20Sopenharmony_ci dwc3_otgregs_init(dwc); 3978c2ecf20Sopenharmony_ci dwc3_otg_device_init(dwc); 3988c2ecf20Sopenharmony_ci dwc3_event_buffers_setup(dwc); 3998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dwc->lock, flags); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (dwc->usb2_phy) 4028c2ecf20Sopenharmony_ci otg_set_vbus(dwc->usb2_phy->otg, false); 4038c2ecf20Sopenharmony_ci if (dwc->usb2_generic_phy) 4048c2ecf20Sopenharmony_ci phy_set_mode(dwc->usb2_generic_phy, 4058c2ecf20Sopenharmony_ci PHY_MODE_USB_DEVICE); 4068c2ecf20Sopenharmony_ci ret = dwc3_gadget_init(dwc); 4078c2ecf20Sopenharmony_ci if (ret) 4088c2ecf20Sopenharmony_ci dev_err(dwc->dev, "failed to initialize peripheral\n"); 4098c2ecf20Sopenharmony_ci break; 4108c2ecf20Sopenharmony_ci default: 4118c2ecf20Sopenharmony_ci break; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci} 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_cistatic void dwc3_drd_update(struct dwc3 *dwc) 4168c2ecf20Sopenharmony_ci{ 4178c2ecf20Sopenharmony_ci int id; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (dwc->edev) { 4208c2ecf20Sopenharmony_ci id = extcon_get_state(dwc->edev, EXTCON_USB_HOST); 4218c2ecf20Sopenharmony_ci if (id < 0) 4228c2ecf20Sopenharmony_ci id = 0; 4238c2ecf20Sopenharmony_ci dwc3_set_mode(dwc, id ? 4248c2ecf20Sopenharmony_ci DWC3_GCTL_PRTCAP_HOST : 4258c2ecf20Sopenharmony_ci DWC3_GCTL_PRTCAP_DEVICE); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic int dwc3_drd_notifier(struct notifier_block *nb, 4308c2ecf20Sopenharmony_ci unsigned long event, void *ptr) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct dwc3 *dwc = container_of(nb, struct dwc3, edev_nb); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci dwc3_set_mode(dwc, event ? 4358c2ecf20Sopenharmony_ci DWC3_GCTL_PRTCAP_HOST : 4368c2ecf20Sopenharmony_ci DWC3_GCTL_PRTCAP_DEVICE); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return NOTIFY_DONE; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci struct device *dev = dwc->dev; 4448c2ecf20Sopenharmony_ci struct device_node *np_phy, *np_conn; 4458c2ecf20Sopenharmony_ci struct extcon_dev *edev; 4468c2ecf20Sopenharmony_ci const char *name; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (device_property_read_bool(dev, "extcon")) 4498c2ecf20Sopenharmony_ci return extcon_get_edev_by_phandle(dev, 0); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* 4528c2ecf20Sopenharmony_ci * Device tree platforms should get extcon via phandle. 4538c2ecf20Sopenharmony_ci * On ACPI platforms, we get the name from a device property. 4548c2ecf20Sopenharmony_ci * This device property is for kernel internal use only and 4558c2ecf20Sopenharmony_ci * is expected to be set by the glue code. 4568c2ecf20Sopenharmony_ci */ 4578c2ecf20Sopenharmony_ci if (device_property_read_string(dev, "linux,extcon-name", &name) == 0) { 4588c2ecf20Sopenharmony_ci edev = extcon_get_extcon_dev(name); 4598c2ecf20Sopenharmony_ci if (!edev) 4608c2ecf20Sopenharmony_ci return ERR_PTR(-EPROBE_DEFER); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci return edev; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci np_phy = of_parse_phandle(dev->of_node, "phys", 0); 4668c2ecf20Sopenharmony_ci np_conn = of_graph_get_remote_node(np_phy, -1, -1); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (np_conn) 4698c2ecf20Sopenharmony_ci edev = extcon_find_edev_by_node(np_conn); 4708c2ecf20Sopenharmony_ci else 4718c2ecf20Sopenharmony_ci edev = NULL; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci of_node_put(np_conn); 4748c2ecf20Sopenharmony_ci of_node_put(np_phy); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return edev; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_USB_ROLE_SWITCH) 4808c2ecf20Sopenharmony_ci#define ROLE_SWITCH 1 4818c2ecf20Sopenharmony_cistatic int dwc3_usb_role_switch_set(struct usb_role_switch *sw, 4828c2ecf20Sopenharmony_ci enum usb_role role) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct dwc3 *dwc = usb_role_switch_get_drvdata(sw); 4858c2ecf20Sopenharmony_ci u32 mode; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci switch (role) { 4888c2ecf20Sopenharmony_ci case USB_ROLE_HOST: 4898c2ecf20Sopenharmony_ci mode = DWC3_GCTL_PRTCAP_HOST; 4908c2ecf20Sopenharmony_ci break; 4918c2ecf20Sopenharmony_ci case USB_ROLE_DEVICE: 4928c2ecf20Sopenharmony_ci mode = DWC3_GCTL_PRTCAP_DEVICE; 4938c2ecf20Sopenharmony_ci break; 4948c2ecf20Sopenharmony_ci default: 4958c2ecf20Sopenharmony_ci if (dwc->role_switch_default_mode == USB_DR_MODE_HOST) 4968c2ecf20Sopenharmony_ci mode = DWC3_GCTL_PRTCAP_HOST; 4978c2ecf20Sopenharmony_ci else 4988c2ecf20Sopenharmony_ci mode = DWC3_GCTL_PRTCAP_DEVICE; 4998c2ecf20Sopenharmony_ci break; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci dwc3_set_mode(dwc, mode); 5038c2ecf20Sopenharmony_ci return 0; 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_cistatic enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci struct dwc3 *dwc = usb_role_switch_get_drvdata(sw); 5098c2ecf20Sopenharmony_ci unsigned long flags; 5108c2ecf20Sopenharmony_ci enum usb_role role; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci spin_lock_irqsave(&dwc->lock, flags); 5138c2ecf20Sopenharmony_ci switch (dwc->current_dr_role) { 5148c2ecf20Sopenharmony_ci case DWC3_GCTL_PRTCAP_HOST: 5158c2ecf20Sopenharmony_ci role = USB_ROLE_HOST; 5168c2ecf20Sopenharmony_ci break; 5178c2ecf20Sopenharmony_ci case DWC3_GCTL_PRTCAP_DEVICE: 5188c2ecf20Sopenharmony_ci role = USB_ROLE_DEVICE; 5198c2ecf20Sopenharmony_ci break; 5208c2ecf20Sopenharmony_ci case DWC3_GCTL_PRTCAP_OTG: 5218c2ecf20Sopenharmony_ci role = dwc->current_otg_role; 5228c2ecf20Sopenharmony_ci break; 5238c2ecf20Sopenharmony_ci default: 5248c2ecf20Sopenharmony_ci if (dwc->role_switch_default_mode == USB_DR_MODE_HOST) 5258c2ecf20Sopenharmony_ci role = USB_ROLE_HOST; 5268c2ecf20Sopenharmony_ci else 5278c2ecf20Sopenharmony_ci role = USB_ROLE_DEVICE; 5288c2ecf20Sopenharmony_ci break; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dwc->lock, flags); 5318c2ecf20Sopenharmony_ci return role; 5328c2ecf20Sopenharmony_ci} 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic int dwc3_setup_role_switch(struct dwc3 *dwc) 5358c2ecf20Sopenharmony_ci{ 5368c2ecf20Sopenharmony_ci struct usb_role_switch_desc dwc3_role_switch = {NULL}; 5378c2ecf20Sopenharmony_ci const char *str; 5388c2ecf20Sopenharmony_ci u32 mode; 5398c2ecf20Sopenharmony_ci int ret; 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci ret = device_property_read_string(dwc->dev, "role-switch-default-mode", 5428c2ecf20Sopenharmony_ci &str); 5438c2ecf20Sopenharmony_ci if (ret >= 0 && !strncmp(str, "host", strlen("host"))) { 5448c2ecf20Sopenharmony_ci dwc->role_switch_default_mode = USB_DR_MODE_HOST; 5458c2ecf20Sopenharmony_ci mode = DWC3_GCTL_PRTCAP_HOST; 5468c2ecf20Sopenharmony_ci } else { 5478c2ecf20Sopenharmony_ci dwc->role_switch_default_mode = USB_DR_MODE_PERIPHERAL; 5488c2ecf20Sopenharmony_ci mode = DWC3_GCTL_PRTCAP_DEVICE; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci dwc3_set_mode(dwc, mode); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci dwc3_role_switch.fwnode = dev_fwnode(dwc->dev); 5538c2ecf20Sopenharmony_ci dwc3_role_switch.set = dwc3_usb_role_switch_set; 5548c2ecf20Sopenharmony_ci dwc3_role_switch.get = dwc3_usb_role_switch_get; 5558c2ecf20Sopenharmony_ci dwc3_role_switch.driver_data = dwc; 5568c2ecf20Sopenharmony_ci dwc->role_sw = usb_role_switch_register(dwc->dev, &dwc3_role_switch); 5578c2ecf20Sopenharmony_ci if (IS_ERR(dwc->role_sw)) 5588c2ecf20Sopenharmony_ci return PTR_ERR(dwc->role_sw); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci return 0; 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci#else 5638c2ecf20Sopenharmony_ci#define ROLE_SWITCH 0 5648c2ecf20Sopenharmony_ci#define dwc3_setup_role_switch(x) 0 5658c2ecf20Sopenharmony_ci#endif 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ciint dwc3_drd_init(struct dwc3 *dwc) 5688c2ecf20Sopenharmony_ci{ 5698c2ecf20Sopenharmony_ci int ret, irq; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci if (ROLE_SWITCH && 5728c2ecf20Sopenharmony_ci device_property_read_bool(dwc->dev, "usb-role-switch")) 5738c2ecf20Sopenharmony_ci return dwc3_setup_role_switch(dwc); 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci dwc->edev = dwc3_get_extcon(dwc); 5768c2ecf20Sopenharmony_ci if (IS_ERR(dwc->edev)) 5778c2ecf20Sopenharmony_ci return PTR_ERR(dwc->edev); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci if (dwc->edev) { 5808c2ecf20Sopenharmony_ci dwc->edev_nb.notifier_call = dwc3_drd_notifier; 5818c2ecf20Sopenharmony_ci ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST, 5828c2ecf20Sopenharmony_ci &dwc->edev_nb); 5838c2ecf20Sopenharmony_ci if (ret < 0) { 5848c2ecf20Sopenharmony_ci dev_err(dwc->dev, "couldn't register cable notifier\n"); 5858c2ecf20Sopenharmony_ci return ret; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci dwc3_drd_update(dwc); 5898c2ecf20Sopenharmony_ci } else { 5908c2ecf20Sopenharmony_ci dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_OTG); 5918c2ecf20Sopenharmony_ci dwc->current_dr_role = DWC3_GCTL_PRTCAP_OTG; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci /* use OTG block to get ID event */ 5948c2ecf20Sopenharmony_ci irq = dwc3_otg_get_irq(dwc); 5958c2ecf20Sopenharmony_ci if (irq < 0) 5968c2ecf20Sopenharmony_ci return irq; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci dwc->otg_irq = irq; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci /* disable all OTG IRQs */ 6018c2ecf20Sopenharmony_ci dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS); 6028c2ecf20Sopenharmony_ci /* clear all events */ 6038c2ecf20Sopenharmony_ci dwc3_otg_clear_events(dwc); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci ret = request_threaded_irq(dwc->otg_irq, dwc3_otg_irq, 6068c2ecf20Sopenharmony_ci dwc3_otg_thread_irq, 6078c2ecf20Sopenharmony_ci IRQF_SHARED, "dwc3-otg", dwc); 6088c2ecf20Sopenharmony_ci if (ret) { 6098c2ecf20Sopenharmony_ci dev_err(dwc->dev, "failed to request irq #%d --> %d\n", 6108c2ecf20Sopenharmony_ci dwc->otg_irq, ret); 6118c2ecf20Sopenharmony_ci ret = -ENODEV; 6128c2ecf20Sopenharmony_ci return ret; 6138c2ecf20Sopenharmony_ci } 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci dwc3_otg_init(dwc); 6168c2ecf20Sopenharmony_ci dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); 6178c2ecf20Sopenharmony_ci } 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci return 0; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_civoid dwc3_drd_exit(struct dwc3 *dwc) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci unsigned long flags; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci if (dwc->role_sw) 6278c2ecf20Sopenharmony_ci usb_role_switch_unregister(dwc->role_sw); 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci if (dwc->edev) 6308c2ecf20Sopenharmony_ci extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST, 6318c2ecf20Sopenharmony_ci &dwc->edev_nb); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci cancel_work_sync(&dwc->drd_work); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci /* debug user might have changed role, clean based on current role */ 6368c2ecf20Sopenharmony_ci switch (dwc->current_dr_role) { 6378c2ecf20Sopenharmony_ci case DWC3_GCTL_PRTCAP_HOST: 6388c2ecf20Sopenharmony_ci dwc3_host_exit(dwc); 6398c2ecf20Sopenharmony_ci break; 6408c2ecf20Sopenharmony_ci case DWC3_GCTL_PRTCAP_DEVICE: 6418c2ecf20Sopenharmony_ci dwc3_gadget_exit(dwc); 6428c2ecf20Sopenharmony_ci dwc3_event_buffers_cleanup(dwc); 6438c2ecf20Sopenharmony_ci break; 6448c2ecf20Sopenharmony_ci case DWC3_GCTL_PRTCAP_OTG: 6458c2ecf20Sopenharmony_ci dwc3_otg_exit(dwc); 6468c2ecf20Sopenharmony_ci spin_lock_irqsave(&dwc->lock, flags); 6478c2ecf20Sopenharmony_ci dwc->desired_otg_role = DWC3_OTG_ROLE_IDLE; 6488c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dwc->lock, flags); 6498c2ecf20Sopenharmony_ci dwc3_otg_update(dwc, 1); 6508c2ecf20Sopenharmony_ci break; 6518c2ecf20Sopenharmony_ci default: 6528c2ecf20Sopenharmony_ci break; 6538c2ecf20Sopenharmony_ci } 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (dwc->otg_irq) 6568c2ecf20Sopenharmony_ci free_irq(dwc->otg_irq, dwc); 6578c2ecf20Sopenharmony_ci} 658