Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1419390 > unrolled thread
| Started by | Roger Quadros <rogerq@ti.com> |
|---|---|
| First post | 2016-06-10 15:20 +0200 |
| Last post | 2016-06-13 10:30 +0200 |
| Articles | 4 — 2 participants |
Back to article view | Back to linux.kernel
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
[PATCH v7 2/4] usb: dwc3: add dual-role support Roger Quadros <rogerq@ti.com> - 2016-06-10 15:20 +0200
Re: [PATCH v7 2/4] usb: dwc3: add dual-role support Peter Chen <hzpeterchen@gmail.com> - 2016-06-12 11:20 +0200
Re: [PATCH v7 2/4] usb: dwc3: add dual-role support Roger Quadros <rogerq@ti.com> - 2016-06-13 09:10 +0200
Re: [PATCH v7 2/4] usb: dwc3: add dual-role support Roger Quadros <rogerq@ti.com> - 2016-06-13 10:30 +0200
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-10 15:20 +0200 |
| Subject | [PATCH v7 2/4] usb: dwc3: add dual-role support |
| Message-ID | <rIuvT-527-1@gated-at.bofh.it> |
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
[toc] | [next] | [standalone]
| From | Peter Chen <hzpeterchen@gmail.com> |
|---|---|
| Date | 2016-06-12 11:20 +0200 |
| Message-ID | <rJ9IJ-6Nw-9@gated-at.bofh.it> |
| In reply to | #1419390 |
On Fri, Jun 10, 2016 at 04:17:28PM +0300, Roger Quadros wrote:
> 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.
>
Hi Roger,
I can't apply it after rebase usb-next rc1 and felipe's testing/next.
How to apply it?
Peter
> 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
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Best Regards,
Peter Chen
[toc] | [prev] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-13 09:10 +0200 |
| Message-ID | <rJuau-2XT-15@gated-at.bofh.it> |
| In reply to | #1420205 |
Peter, On 12/06/16 12:11, Peter Chen wrote: > On Fri, Jun 10, 2016 at 04:17:28PM +0300, Roger Quadros wrote: >> 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. >> > > Hi Roger, > > I can't apply it after rebase usb-next rc1 and felipe's testing/next. > How to apply it? You just need to apply the series on Felipe's testing/next + this one patch http://article.gmane.org/gmane.linux.kernel/2240761 -- cheers, -roger
[toc] | [prev] | [next] | [standalone]
| From | Roger Quadros <rogerq@ti.com> |
|---|---|
| Date | 2016-06-13 10:30 +0200 |
| Message-ID | <rJvpT-3Ie-3@gated-at.bofh.it> |
| In reply to | #1420499 |
On 13/06/16 10:09, Roger Quadros wrote: > Peter, > > On 12/06/16 12:11, Peter Chen wrote: >> On Fri, Jun 10, 2016 at 04:17:28PM +0300, Roger Quadros wrote: >>> 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. >>> >> >> Hi Roger, >> >> I can't apply it after rebase usb-next rc1 and felipe's testing/next. >> How to apply it? > > You just need to apply the series on Felipe's testing/next + this one patch > http://article.gmane.org/gmane.linux.kernel/2240761 You can also use the below repo as balbi testing/next is a moving target https://github.com/rogerq/linux/commits/for-v4.8/otg-drd-v11-core-dwc3-v7 -- cheers, -roger
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web