lkml.org 
[lkml]   [2016]   [Jun]   [10]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH v7 2/4] usb: dwc3: add dual-role support
    Date
    Register with the USB OTG/DRD core. Since we don't support
    OTG yet we just work as a dual-role device even
    if device tree says "otg".

    Get ID and VBUS information from the OTG controller
    and kick the OTG state machine.

    Signed-off-by: Roger Quadros <rogerq@ti.com>
    ---
    drivers/usb/dwc3/core.c | 546 +++++++++++++++++++++++++++++++++++++++++++++-
    drivers/usb/dwc3/core.h | 30 ++-
    drivers/usb/dwc3/gadget.c | 6 +-
    drivers/usb/dwc3/host.c | 2 +
    4 files changed, 574 insertions(+), 10 deletions(-)

    diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
    index d51c9a9..28d2da2 100644
    --- a/drivers/usb/dwc3/core.c
    +++ b/drivers/usb/dwc3/core.c
    @@ -56,6 +56,7 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
    reg = dwc3_readl(dwc->regs, DWC3_GCTL);
    reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
    reg |= DWC3_GCTL_PRTCAPDIR(mode);
    + dwc->current_mode = mode;
    dwc3_writel(dwc->regs, DWC3_GCTL, reg);
    }

    @@ -756,6 +757,448 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
    return 0;
    }

    +/* Get OTG events and sync it to OTG fsm */
    +static void dwc3_otg_fsm_sync(struct dwc3 *dwc)
    +{
    + u32 reg;
    + int id, vbus;
    +
    + /*
    + * calling usb_otg_sync_inputs() during resume breaks host
    + * if adapter was removed during suspend as xhci driver
    + * is not prepared to see hcd removal before xhci_resume.
    + */
    + if (dwc->otg_prevent_sync)
    + return;
    +
    + reg = dwc3_readl(dwc->regs, DWC3_OSTS);
    + dwc3_trace(trace_dwc3_core, "otgstatus 0x%x\n", reg);
    +
    + id = !!(reg & DWC3_OSTS_CONIDSTS);
    + vbus = !!(reg & DWC3_OSTS_BSESVLD);
    +
    + dwc3_trace(trace_dwc3_core, "id %d vbus %d\n", id, vbus);
    + dwc->otg->fsm.id = id;
    + dwc->otg->fsm.b_sess_vld = vbus;
    + usb_otg_sync_inputs(dwc->otg);
    +}
    +
    +static void dwc3_otg_mask_irq(struct dwc3 *dwc)
    +{
    + dwc->oevten = dwc3_readl(dwc->regs, DWC3_OEVTEN);
    + dwc3_writel(dwc->regs, DWC3_OEVTEN, 0);
    +}
    +
    +static void dwc3_otg_unmask_irq(struct dwc3 *dwc)
    +{
    + dwc3_writel(dwc->regs, DWC3_OEVTEN, dwc->oevten);
    +}
    +
    +static void dwc3_otg_disable_events(struct dwc3 *dwc, u32 disable_mask)
    +{
    + dwc->oevten &= ~(disable_mask);
    + dwc3_writel(dwc->regs, DWC3_OEVTEN, dwc->oevten);
    +}
    +
    +static void dwc3_otg_enable_events(struct dwc3 *dwc, u32 enable_mask)
    +{
    + dwc->oevten |= (enable_mask);
    + dwc3_writel(dwc->regs, DWC3_OEVTEN, dwc->oevten);
    +}
    +
    +#define DWC3_OTG_ALL_EVENTS (DWC3_OEVTEN_XHCIRUNSTPSETEN | \
    + DWC3_OEVTEN_DEVRUNSTPSETEN | DWC3_OEVTEN_HIBENTRYEN | \
    + DWC3_OEVTEN_CONIDSTSCHNGEN | DWC3_OEVTEN_HRRCONFNOTIFEN | \
    + DWC3_OEVTEN_HRRINITNOTIFEN | DWC3_OEVTEN_ADEVIDLEEN | \
    + DWC3_OEVTEN_ADEVBHOSTENDEN | DWC3_OEVTEN_ADEVHOSTEN | \
    + DWC3_OEVTEN_ADEVHNPCHNGEN | DWC3_OEVTEN_ADEVSRPDETEN | \
    + DWC3_OEVTEN_ADEVSESSENDDETEN | DWC3_OEVTEN_BDEVHOSTENDEN | \
    + DWC3_OEVTEN_BDEVHNPCHNGEN | DWC3_OEVTEN_BDEVSESSVLDDETEN | \
    + DWC3_OEVTEN_BDEVVBUSCHNGE)
    +
    +static int dwc3_drd_start_host(struct usb_otg *otg, int on);
    +static int dwc3_drd_start_gadget(struct usb_otg *otg, int on);
    +static irqreturn_t dwc3_otg_thread_irq(int irq, void *_dwc)
    +{
    + struct dwc3 *dwc = _dwc;
    + unsigned long flags;
    +
    + spin_lock_irqsave(&dwc->lock, flags);
    +
    + /*
    + * this bit is needed for otg-host to work after system suspend/resume
    + */
    + if ((dwc->otg->state == OTG_STATE_A_HOST) &&
    + !(dwc->oevt & DWC3_OEVT_DEVICEMODE)) {
    + spin_unlock_irqrestore(&dwc->lock, flags);
    + dwc3_drd_start_host(dwc->otg, true);
    + spin_lock_irqsave(&dwc->lock, flags);
    + }
    +
    + dwc3_otg_fsm_sync(dwc);
    + dwc3_otg_unmask_irq(dwc);
    +
    + dwc->oevt = 0;
    + spin_unlock_irqrestore(&dwc->lock, flags);
    +
    + return IRQ_HANDLED;
    +}
    +
    +static irqreturn_t dwc3_otg_irq(int irq, void *_dwc)
    +{
    + struct dwc3 *dwc = _dwc;
    + irqreturn_t ret = IRQ_NONE;
    + u32 reg;
    +
    + reg = dwc3_readl(dwc->regs, DWC3_OEVT);
    + if (reg) {
    + dwc->oevt = reg;
    + dwc3_writel(dwc->regs, DWC3_OEVT, reg);
    + dwc3_otg_mask_irq(dwc);
    + ret = IRQ_WAKE_THREAD;
    + }
    +
    + return ret;
    +}
    +
    +/* --------------------- Dual-Role management ------------------------------- */
    +static void dwc3_otgregs_init(struct dwc3 *dwc)
    +{
    + u32 reg;
    +
    + /*
    + * Prevent host/device reset from resetting OTG core.
    + * If we don't do this then xhci_reset (USBCMD.HCRST) will reset
    + * the signal outputs sent to the PHY, the OTG FSM logic of the
    + * core and also the resets to the VBUS filters inside the core.
    + */
    + reg = dwc3_readl(dwc->regs, DWC3_OCFG);
    + reg |= DWC3_OCFG_SFTRSTMASK;
    + dwc3_writel(dwc->regs, DWC3_OCFG, reg);
    +
    + /* Disable hibernation for simplicity */
    + reg = dwc3_readl(dwc->regs, DWC3_GCTL);
    + reg &= ~DWC3_GCTL_GBLHIBERNATIONEN;
    + dwc3_writel(dwc->regs, DWC3_GCTL, reg);
    +
    + /*
    + * Initialize OTG registers as per
    + * Figure 11-4 OTG Driver Overall Programming Flow
    + */
    + /* OCFG.SRPCap = 0, OCFG.HNPCap = 0 */
    + reg = dwc3_readl(dwc->regs, DWC3_OCFG);
    + reg &= ~(DWC3_OCFG_SRPCAP | DWC3_OCFG_HNPCAP);
    + dwc3_writel(dwc->regs, DWC3_OCFG, reg);
    + /* OEVT = FFFF */
    + dwc3_writel(dwc->regs, DWC3_OEVT, ~0);
    + /* OEVTEN = 0 */
    + dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS);
    + /* OEVTEN.ConIDStsChngEn = 1. Instead we enable all events */
    + dwc3_otg_enable_events(dwc, DWC3_OTG_ALL_EVENTS);
    + /*
    + * OCTL.PeriMode = 1, OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0,
    + * OCTL.HNPReq = 0
    + */
    + reg = dwc3_readl(dwc->regs, DWC3_OCTL);
    + reg |= DWC3_OCTL_PERIMODE;
    + reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN |
    + DWC3_OCTL_HNPREQ);
    + dwc3_writel(dwc->regs, DWC3_OCTL, reg);
    +}
    +
    +static int dwc3_drd_start_host(struct usb_otg *otg, int on)
    +{
    + struct dwc3 *dwc = dev_get_drvdata(otg->dev);
    + u32 reg;
    + unsigned long flags;
    +
    + dwc3_trace(trace_dwc3_core, "%s: %d\n", __func__, on);
    +
    + /* switch OTG core */
    + if (on) {
    + /* As per Figure 11-10 A-Device Flow Diagram */
    +
    + spin_lock_irqsave(&dwc->lock, flags);
    + /* OCFG.HNPCap = 0, OCFG.SRPCap = 0 */
    + reg = dwc3_readl(dwc->regs, DWC3_OCFG);
    + reg &= ~(DWC3_OCFG_SRPCAP | DWC3_OCFG_HNPCAP);
    + dwc3_writel(dwc->regs, DWC3_OCFG, reg);
    +
    + /*
    + * OCTL.PeriMode=0, OCTL.TermSelDLPulse = 0,
    + * OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0
    + */
    + reg = dwc3_readl(dwc->regs, DWC3_OCTL);
    + reg &= ~(DWC3_OCTL_PERIMODE | DWC3_OCTL_TERMSELIDPULSE |
    + DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN);
    + dwc3_writel(dwc->regs, DWC3_OCTL, reg);
    +
    + /*
    + * OCFG.DisPrtPwrCutoff = 0/1
    + */
    + reg = dwc3_readl(dwc->regs, DWC3_OCFG);
    + reg &= ~DWC3_OCFG_DISPWRCUTTOFF;
    + dwc3_writel(dwc->regs, DWC3_OCFG, reg);
    +
    + /* start the xHCI host driver */
    + spin_unlock_irqrestore(&dwc->lock, flags);
    + usb_otg_start_host(otg, true);
    + spin_lock_irqsave(&dwc->lock, flags);
    +
    + /*
    + * OCFG.SRPCap = 1, OCFG.HNPCap = GHWPARAMS6.HNP_CAP
    + * We don't want SRP/HNP for simple dual-role so leave
    + * these disabled.
    + */
    +
    + /*
    + * OEVTEN.OTGADevHostEvntEn = 1
    + * OEVTEN.OTGADevSessEndDetEvntEn = 1
    + * We don't want HNP/role-swap so leave these disabled.
    + */
    +
    + /* GUSB2PHYCFG.ULPIAutoRes = 1/0, GUSB2PHYCFG.SusPHY = 1 */
    + if (!dwc->dis_u2_susphy_quirk) {
    + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
    + reg |= DWC3_GUSB2PHYCFG_SUSPHY;
    + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
    + }
    +
    + /* Set Port Power to enable VBUS: OCTL.PrtPwrCtl = 1 */
    + reg = dwc3_readl(dwc->regs, DWC3_OCTL);
    + reg |= DWC3_OCTL_PRTPWRCTL;
    + dwc3_writel(dwc->regs, DWC3_OCTL, reg);
    + spin_unlock_irqrestore(&dwc->lock, flags);
    + } else {
    + /*
    + * Exit from A-device flow as per
    + * Figure 11-4 OTG Driver Overall Programming Flow
    + */
    + /* stop the HCD */
    + usb_otg_start_host(otg, false);
    +
    + spin_lock_irqsave(&dwc->lock, flags);
    + /*
    + * OEVTEN.OTGADevBHostEndEvntEn=0, OEVTEN.OTGADevHNPChngEvntEn=0
    + * OEVTEN.OTGADevSessEndDetEvntEn=0,
    + * OEVTEN.OTGADevHostEvntEn = 0
    + * But we don't disable any OTG events
    + */
    +
    + /* OCTL.HstSetHNPEn = 0, OCTL.PrtPwrCtl=0 */
    + reg = dwc3_readl(dwc->regs, DWC3_OCTL);
    + reg &= ~(DWC3_OCTL_HSTSETHNPEN | DWC3_OCTL_PRTPWRCTL);
    + dwc3_writel(dwc->regs, DWC3_OCTL, reg);
    +
    + /* Initialize OTG registers */
    + dwc3_otgregs_init(dwc);
    + spin_unlock_irqrestore(&dwc->lock, flags);
    + }
    +
    + return 0;
    +}
    +
    +static int dwc3_drd_start_gadget(struct usb_otg *otg, int on)
    +{
    + struct dwc3 *dwc = dev_get_drvdata(otg->dev);
    + u32 reg;
    + unsigned long flags;
    +
    + dwc3_trace(trace_dwc3_core, "%s: %d\n", __func__, on);
    + if (on)
    + dwc3_event_buffers_setup(dwc);
    +
    + if (on) {
    + /* As per Figure 11-20 B-Device Flow Diagram */
    +
    + spin_lock_irqsave(&dwc->lock, flags);
    + /*
    + * OCFG.HNPCap = GHWPARAMS6.HNP_CAP, OCFG.SRPCap = 1
    + * but we set them to 0 for simple dual-role operation.
    + */
    + reg = dwc3_readl(dwc->regs, DWC3_OCFG);
    + reg &= ~(DWC3_OCFG_SRPCAP | DWC3_OCFG_HNPCAP);
    + /* OCFG.OTGSftRstMsk = 0/1 */
    + reg |= DWC3_OCFG_SFTRSTMASK;
    + dwc3_writel(dwc->regs, DWC3_OCFG, reg);
    + /*
    + * OCTL.PeriMode = 1
    + * OCTL.TermSelDLPulse = 0/1, OCTL.HNPReq = 0
    + * OCTL.DevSetHNPEn = 0, OCTL.HstSetHNPEn = 0
    + */
    + reg = dwc3_readl(dwc->regs, DWC3_OCTL);
    + reg |= DWC3_OCTL_PERIMODE;
    + reg &= ~(DWC3_OCTL_TERMSELIDPULSE | DWC3_OCTL_HNPREQ |
    + DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HSTSETHNPEN);
    + dwc3_writel(dwc->regs, DWC3_OCTL, reg);
    + /* OEVTEN.OTGBDevSesVldDetEvntEn = 1 */
    + dwc3_otg_enable_events(dwc, DWC3_OEVT_BDEVSESSVLDDET);
    + /* GUSB2PHYCFG.ULPIAutoRes = 0, GUSB2PHYCFG0.SusPHY = 1 */
    + if (!dwc->dis_u2_susphy_quirk) {
    + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
    + reg |= DWC3_GUSB2PHYCFG_SUSPHY;
    + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
    + }
    + /* GCTL.GblHibernationEn = 0 */
    + reg = dwc3_readl(dwc->regs, DWC3_GCTL);
    + reg &= ~DWC3_GCTL_GBLHIBERNATIONEN;
    + dwc3_writel(dwc->regs, DWC3_GCTL, reg);
    +
    + spin_unlock_irqrestore(&dwc->lock, flags);
    +
    + /* start the Peripheral driver */
    + usb_otg_start_gadget(otg, true);
    + } else {
    + /*
    + * Exit from B-device flow as per
    + * Figure 11-4 OTG Driver Overall Programming Flow
    + */
    + /* stop the Peripheral driver */
    + usb_otg_start_gadget(otg, false);
    +
    + spin_lock_irqsave(&dwc->lock, flags);
    +
    + /*
    + * OEVTEN.OTGBDevHNPChngEvntEn = 0
    + * OEVTEN.OTGBDevVBusChngEvntEn = 0
    + * OEVTEN.OTGBDevBHostEndEvntEn = 0
    + */
    + reg = dwc3_readl(dwc->regs, DWC3_OEVTEN);
    + reg &= ~(DWC3_OEVT_BDEVHNPCHNG | DWC3_OEVT_BDEVVBUSCHNG |
    + DWC3_OEVT_BDEVBHOSTEND);
    + dwc3_writel(dwc->regs, DWC3_OEVTEN, reg);
    +
    + /* OCTL.DevSetHNPEn = 0, OCTL.HNPReq = 0, OCTL.PeriMode=1 */
    + reg = dwc3_readl(dwc->regs, DWC3_OCTL);
    + reg &= ~(DWC3_OCTL_DEVSETHNPEN | DWC3_OCTL_HNPREQ);
    + reg |= DWC3_OCTL_PERIMODE;
    + dwc3_writel(dwc->regs, DWC3_OCTL, reg);
    +
    + /* Initialize OTG registers */
    + dwc3_otgregs_init(dwc);
    + spin_unlock_irqrestore(&dwc->lock, flags);
    + }
    +
    + return 0;
    +}
    +
    +static struct otg_fsm_ops dwc3_drd_ops = {
    + .start_host = dwc3_drd_start_host,
    + .start_gadget = dwc3_drd_start_gadget,
    +};
    +
    +static int dwc3_drd_register(struct dwc3 *dwc)
    +{
    + int ret;
    +
    + /* register parent as DRD device with OTG core */
    + dwc->otg = usb_otg_register(dwc->dev, &dwc->otg_config);
    + if (IS_ERR(dwc->otg)) {
    + ret = PTR_ERR(dwc->otg);
    + if (ret == -ENOTSUPP)
    + dev_err(dwc->dev, "CONFIG_USB_OTG needed for dual-role\n");
    + else
    + dev_err(dwc->dev, "Failed to register with OTG core\n");
    +
    + return ret;
    + }
    +
    + return 0;
    +}
    +
    +static int dwc3_drd_init(struct dwc3 *dwc)
    +{
    + int ret, irq;
    + struct usb_otg_caps *otgcaps = &dwc->otg_caps;
    + u32 reg;
    + unsigned long flags;
    + struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
    +
    + irq = platform_get_irq_byname(dwc3_pdev, "otg");
    + if (irq == -EPROBE_DEFER)
    + return irq;
    +
    + if (irq <= 0) {
    + irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3");
    + if (irq == -EPROBE_DEFER)
    + return irq;
    +
    + if (irq <= 0) {
    + irq = platform_get_irq(dwc3_pdev, 0);
    + if (irq <= 0) {
    + if (irq != -EPROBE_DEFER)
    + dev_err(dwc->dev, "missing otg IRQ\n");
    +
    + if (!irq)
    + irq = -EINVAL;
    + return irq;
    + }
    + }
    + }
    +
    + dwc->otg_irq = irq;
    +
    + otgcaps->otg_rev = 0;
    + otgcaps->hnp_support = false;
    + otgcaps->srp_support = false;
    + otgcaps->adp_support = false;
    + dwc->otg_config.fsm_ops = &dwc3_drd_ops;
    + dwc->otg_config.otg_caps = otgcaps;
    +
    + ret = dwc3_drd_register(dwc);
    + if (ret)
    + return ret;
    +
    + /* disable all otg irqs */
    + dwc3_otg_disable_events(dwc, DWC3_OTG_ALL_EVENTS);
    + /* clear all events */
    + dwc3_writel(dwc->regs, DWC3_OEVT, ~0);
    +
    + ret = request_threaded_irq(dwc->otg_irq, dwc3_otg_irq,
    + dwc3_otg_thread_irq,
    + IRQF_SHARED, "dwc3-otg", dwc);
    + if (ret) {
    + dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
    + dwc->otg_irq, ret);
    + ret = -ENODEV;
    + goto error;
    + }
    +
    + spin_lock_irqsave(&dwc->lock, flags);
    +
    + /*
    + * As per Figure 11-4 OTG Driver Overall Programming Flow,
    + * block "Initialize GCTL for OTG operation".
    + */
    + /* GCTL.PrtCapDir=2'b11 */
    + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
    + /* GUSB2PHYCFG0.SusPHY=0 */
    + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
    + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
    + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
    +
    + /* Initialize OTG registers */
    + dwc3_otgregs_init(dwc);
    + spin_unlock_irqrestore(&dwc->lock, flags);
    +
    + dwc3_otg_fsm_sync(dwc);
    +
    + return 0;
    +
    +error:
    + usb_otg_unregister(dwc->dev);
    +
    + return ret;
    +}
    +
    +static void dwc3_drd_exit(struct dwc3 *dwc)
    +{
    + free_irq(dwc->otg_irq, dwc);
    + usb_otg_unregister(dwc->dev);
    +}
    +
    +/* -------------------------------------------------------------------------- */
    +
    static int dwc3_core_init_mode(struct dwc3 *dwc)
    {
    struct device *dev = dwc->dev;
    @@ -781,11 +1224,31 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
    }
    break;
    case USB_DR_MODE_OTG:
    - dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
    + ret = dwc3_drd_init(dwc);
    + if (ret) {
    + if (ret == -EPROBE_DEFER)
    + return ret;
    +
    + dev_err(dev,
    + "limiting to peripheral only as dual-role init failed: %d",
    + ret);
    + dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
    + dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
    + ret = dwc3_gadget_init(dwc);
    + if (ret) {
    + if (ret == -EPROBE_DEFER)
    + return ret;
    + dev_err(dev, "failed to initialize gadget\n");
    + return ret;
    + }
    + break;
    + }
    +
    ret = dwc3_host_init(dwc);
    if (ret) {
    if (ret != -EPROBE_DEFER)
    dev_err(dev, "failed to initialize host\n");
    + dwc3_drd_exit(dwc);
    return ret;
    }

    @@ -793,6 +1256,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
    if (ret) {
    if (ret != -EPROBE_DEFER)
    dev_err(dev, "failed to initialize gadget\n");
    + dwc3_host_exit(dwc);
    + dwc3_drd_exit(dwc);
    return ret;
    }
    break;
    @@ -816,6 +1281,7 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
    case USB_DR_MODE_OTG:
    dwc3_host_exit(dwc);
    dwc3_gadget_exit(dwc);
    + dwc3_drd_exit(dwc);
    break;
    default:
    /* do nothing */
    @@ -1091,19 +1557,34 @@ static int dwc3_suspend_common(struct dwc3 *dwc)
    {
    unsigned long flags;

    + spin_lock_irqsave(&dwc->lock, flags);
    +
    switch (dwc->dr_mode) {
    case USB_DR_MODE_PERIPHERAL:
    - case USB_DR_MODE_OTG:
    - spin_lock_irqsave(&dwc->lock, flags);
    dwc3_gadget_suspend(dwc);
    - spin_unlock_irqrestore(&dwc->lock, flags);
    + break;
    + case USB_DR_MODE_OTG:
    + dwc->otg_protocol = dwc->otg->fsm.protocol;
    +
    + switch (dwc->otg->fsm.protocol) {
    + case PROTO_GADGET:
    + dwc3_gadget_suspend(dwc);
    + break;
    + case PROTO_HOST:
    + case PROTO_UNDEF:
    + default:
    + /* nothing */
    + break;
    + }
    break;
    case USB_DR_MODE_HOST:
    + case USB_DR_MODE_UNKNOWN:
    default:
    /* do nothing */
    break;
    }

    + spin_unlock_irqrestore(&dwc->lock, flags);
    dwc3_core_exit(dwc);

    return 0;
    @@ -1118,19 +1599,41 @@ static int dwc3_resume_common(struct dwc3 *dwc)
    if (ret)
    return ret;

    + spin_lock_irqsave(&dwc->lock, flags);
    +
    switch (dwc->dr_mode) {
    case USB_DR_MODE_PERIPHERAL:
    - case USB_DR_MODE_OTG:
    - spin_lock_irqsave(&dwc->lock, flags);
    dwc3_gadget_resume(dwc);
    - spin_unlock_irqrestore(&dwc->lock, flags);
    - /* FALLTHROUGH */
    + break;
    + case USB_DR_MODE_OTG:
    + switch (dwc->otg_protocol) {
    + case PROTO_GADGET:
    + dwc3_gadget_resume(dwc);
    + break;
    + case PROTO_HOST:
    + break;
    + case PROTO_UNDEF:
    + default:
    + /* nothing */
    + break;
    + }
    + break;
    case USB_DR_MODE_HOST:
    + case USB_DR_MODE_UNKNOWN:
    default:
    /* do nothing */
    break;
    }

    + /* Restore OTG state only if we're really using it */
    + if (dwc->current_mode == DWC3_GCTL_PRTCAP_OTG) {
    + dwc3_writel(dwc->regs, DWC3_OCFG, dwc->ocfg);
    + dwc3_writel(dwc->regs, DWC3_OCTL, dwc->octl);
    + dwc3_otg_unmask_irq(dwc);
    + }
    +
    + spin_unlock_irqrestore(&dwc->lock, flags);
    +
    return 0;
    }

    @@ -1185,6 +1688,7 @@ static int dwc3_runtime_resume(struct device *dev)
    dwc3_gadget_process_pending_events(dwc);
    break;
    case USB_DR_MODE_HOST:
    + case USB_DR_MODE_UNKNOWN:
    default:
    /* do nothing */
    break;
    @@ -1219,6 +1723,30 @@ static int dwc3_runtime_idle(struct device *dev)
    #endif /* CONFIG_PM */

    #ifdef CONFIG_PM_SLEEP
    +static int dwc3_prepare(struct device *dev)
    +{
    + struct dwc3 *dwc = dev_get_drvdata(dev);
    + unsigned long flags;
    +
    + spin_lock_irqsave(&dwc->lock, flags);
    + dwc->otg_prevent_sync = true;
    + spin_unlock_irqrestore(&dwc->lock, flags);
    +
    + return 0;
    +}
    +
    +static void dwc3_complete(struct device *dev)
    +{
    + struct dwc3 *dwc = dev_get_drvdata(dev);
    + unsigned long flags;
    +
    + spin_lock_irqsave(&dwc->lock, flags);
    + dwc->otg_prevent_sync = false;
    + spin_unlock_irqrestore(&dwc->lock, flags);
    + if (dwc->dr_mode == USB_DR_MODE_OTG)
    + dwc3_otg_fsm_sync(dwc);
    +}
    +
    static int dwc3_suspend(struct device *dev)
    {
    struct dwc3 *dwc = dev_get_drvdata(dev);
    @@ -1256,6 +1784,8 @@ static const struct dev_pm_ops dwc3_dev_pm_ops = {
    SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
    SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume,
    dwc3_runtime_idle)
    + .prepare = dwc3_prepare,
    + .complete = dwc3_complete,
    };

    #ifdef CONFIG_OF
    diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
    index 32bb7531..e6b771a 100644
    --- a/drivers/usb/dwc3/core.h
    +++ b/drivers/usb/dwc3/core.h
    @@ -31,6 +31,7 @@
    #include <linux/usb/gadget.h>
    #include <linux/usb/otg.h>
    #include <linux/ulpi/interface.h>
    +#include <linux/usb/otg-fsm.h>

    #include <linux/phy/phy.h>

    @@ -817,13 +818,21 @@ struct dwc3_scratchpad_array {
    * @gadget_driver: pointer to the gadget driver
    * @regs: base address for our registers
    * @regs_size: address space size
    + * @dr_mode: requested mode of operation
    + * @otg: usb otg data structure
    + * @otg_caps: otg controller capabilities
    + * @otg_config: otg controller configuration
    + * @otg_prevent_sync: flag to block events to otg fsm
    + * @otg_protocol: saved copy of otg state during suspend
    + * @current_mode: current mode of operation written to PRTCAPDIR
    + * @oevt: cached OEVT register during OTG irq
    * @fladj: frame length adjustment
    * @irq_gadget: peripheral controller's IRQ number
    + * @otg_irq: IRQ number for OTG IRQs
    * @nr_scratch: number of scratch buffers
    * @u1u2: only used on revisions <1.83a for workaround
    * @maximum_speed: maximum speed requested (mainly for testing purposes)
    * @revision: revision register contents
    - * @dr_mode: requested mode of operation
    * @usb2_phy: pointer to USB2 PHY
    * @usb3_phy: pointer to USB3 PHY
    * @usb2_generic_phy: pointer to USB2 PHY
    @@ -831,6 +840,9 @@ struct dwc3_scratchpad_array {
    * @ulpi: pointer to ulpi interface
    * @dcfg: saved contents of DCFG register
    * @gctl: saved contents of GCTL register
    + * @ocfg: saved contents of OCFG register
    + * @octl: saved contents of OCTL register
    + * @oevten: saved contents of OEVTEN register
    * @isoch_delay: wValue from Set Isochronous Delay request;
    * @u2sel: parameter from Set SEL request.
    * @u2pel: parameter from Set SEL request.
    @@ -929,9 +941,25 @@ struct dwc3 {
    size_t regs_size;

    enum usb_dr_mode dr_mode;
    + struct usb_otg *otg;
    + struct usb_otg_caps otg_caps;
    + struct usb_otg_config otg_config;
    + bool otg_prevent_sync;
    + int otg_protocol;
    + u32 current_mode;
    + u32 oevt;

    u32 fladj;
    u32 irq_gadget;
    + int otg_irq;
    +
    + /* used for suspend/resume */
    + u32 dcfg;
    + u32 gctl;
    + u32 ocfg;
    + u32 octl;
    + u32 oevten;
    +
    u32 nr_scratch;
    u32 u1u2;
    u32 maximum_speed;
    diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
    index 1ade5e8..e409b1e 100644
    --- a/drivers/usb/dwc3/gadget.c
    +++ b/drivers/usb/dwc3/gadget.c
    @@ -2974,7 +2974,11 @@ int dwc3_gadget_init(struct dwc3 *dwc)
    if (ret)
    goto err5;

    - ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
    + if (dwc->dr_mode == USB_DR_MODE_OTG)
    + ret = usb_otg_add_gadget_udc(dwc->dev, &dwc->gadget, dwc->dev);
    + else
    + ret = usb_add_gadget_udc(dwc->dev, &dwc->gadget);
    +
    if (ret) {
    dev_err(dwc->dev, "failed to register udc\n");
    goto err5;
    diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
    index 2e960ed..32096ec 100644
    --- a/drivers/usb/dwc3/host.c
    +++ b/drivers/usb/dwc3/host.c
    @@ -91,6 +91,8 @@ int dwc3_host_init(struct dwc3 *dwc)
    memset(&pdata, 0, sizeof(pdata));

    pdata.usb3_lpm_capable = dwc->usb3_lpm_capable;
    + if (dwc->dr_mode == USB_DR_MODE_OTG)
    + pdata.otg_dev = dwc->dev;

    ret = platform_device_add_data(xhci, &pdata, sizeof(pdata));
    if (ret) {
    --
    2.7.4
    \
     
     \ /
      Last update: 2016-06-10 16:01    [W:2.623 / U:21.608 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site