Hi Anton, On Tue, July 17, 2012 9:27 pm, Anton Tikhomirov wrote: > Hi, > >> -----Original Message----- >> From: linux-usb-owner@xxxxxxxxxxxxxxx [mailto:linux-usb- >> owner@xxxxxxxxxxxxxxx] On Behalf Of Ido Shayevitz >> Sent: Thursday, July 12, 2012 1:24 AM >> To: balbi@xxxxxx >> Cc: linux-usb@xxxxxxxxxxxxxxx; idos@xxxxxxxxxxxxxx >> Subject: [RFC/PATCH v2] usb: dwc3: Introduce OTG driver for dwc3 >> >> This is first release of otg driver for the dwc3 Synopsys USB3 core. >> The otg driver implements the otg final state machine and control the >> activation of the device controller or host controller. >> >> In this first implementation, only simple DRD mode is implemented, >> determine if A or B device according to the ID pin as reflected in the >> OSTS.ConIDSts field. >> >> Signed-off-by: Ido Shayevitz <idos@xxxxxxxxxxxxxx> >> >> --- >> drivers/usb/dwc3/Kconfig | 6 +- >> drivers/usb/dwc3/Makefile | 2 + >> drivers/usb/dwc3/core.c | 15 +- >> drivers/usb/dwc3/core.h | 51 ++++- >> drivers/usb/dwc3/dwc3_otg.c | 512 >> ++++++++++++++++++++++++++++++++++++++++++ >> drivers/usb/dwc3/dwc3_otg.h | 38 +++ >> drivers/usb/dwc3/gadget.c | 63 +++++ >> drivers/usb/host/xhci-plat.c | 21 ++ >> drivers/usb/host/xhci.c | 13 +- >> 9 files changed, 708 insertions(+), 13 deletions(-) >> create mode 100644 drivers/usb/dwc3/dwc3_otg.c >> create mode 100644 drivers/usb/dwc3/dwc3_otg.h >> >> diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig >> index d13c60f..0cc108d 100644 >> --- a/drivers/usb/dwc3/Kconfig >> +++ b/drivers/usb/dwc3/Kconfig >> @@ -1,9 +1,9 @@ >> config USB_DWC3 >> tristate "DesignWare USB3 DRD Core Support" >> - depends on (USB && USB_GADGET) >> + depends on (USB || USB_GADGET) >> select USB_OTG_UTILS >> - select USB_GADGET_DUALSPEED >> - select USB_GADGET_SUPERSPEED >> + select USB_GADGET_DUALSPEED if USB_GADGET >> + select USB_GADGET_SUPERSPEED if USB_GADGET >> select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD >> help >> Say Y or M here if your system has a Dual Role SuperSpeed >> diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile >> index d441fe4..ffb3f55 100644 >> --- a/drivers/usb/dwc3/Makefile >> +++ b/drivers/usb/dwc3/Makefile >> @@ -1,11 +1,13 @@ >> ccflags-$(CONFIG_USB_DWC3_DEBUG) := -DDEBUG >> ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG >> +ccflags-y += -Idrivers/usb/host >> >> obj-$(CONFIG_USB_DWC3) += dwc3.o >> >> dwc3-y := core.o >> dwc3-y += host.o >> dwc3-y += gadget.o ep0.o >> +dwc3-y += dwc3_otg.o >> >> ifneq ($(CONFIG_DEBUG_FS),) >> dwc3-y += debugfs.o >> diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c >> index c34452a..5343e39 100644 >> --- a/drivers/usb/dwc3/core.c >> +++ b/drivers/usb/dwc3/core.c >> @@ -517,15 +517,24 @@ static int __devinit dwc3_probe(struct >> platform_device *pdev) >> break; >> case DWC3_MODE_DRD: >> dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); >> + ret = dwc3_otg_init(dwc); >> + if (ret) { >> + dev_err(dev, "failed to initialize otg\n"); >> + goto err1; >> + } >> + >> ret = dwc3_host_init(dwc); >> if (ret) { >> dev_err(dev, "failed to initialize host\n"); >> + dwc3_otg_exit(dwc); >> goto err1; >> } >> >> ret = dwc3_gadget_init(dwc); >> if (ret) { >> dev_err(dev, "failed to initialize gadget\n"); >> + dwc3_host_exit(dwc); >> + dwc3_otg_exit(dwc); >> goto err1; >> } >> break; >> @@ -554,8 +563,9 @@ err2: >> dwc3_host_exit(dwc); >> break; >> case DWC3_MODE_DRD: >> - dwc3_host_exit(dwc); >> dwc3_gadget_exit(dwc); >> + dwc3_host_exit(dwc); >> + dwc3_otg_exit(dwc); >> break; >> default: >> /* do nothing */ >> @@ -588,8 +598,9 @@ static int __devexit dwc3_remove(struct >> platform_device *pdev) >> dwc3_host_exit(dwc); >> break; >> case DWC3_MODE_DRD: >> - dwc3_host_exit(dwc); >> dwc3_gadget_exit(dwc); >> + dwc3_host_exit(dwc); >> + dwc3_otg_exit(dwc); >> break; >> default: >> /* do nothing */ >> diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h >> index 151eca8..793758b 100644 >> --- a/drivers/usb/dwc3/core.h >> +++ b/drivers/usb/dwc3/core.h >> @@ -50,6 +50,8 @@ >> #include <linux/usb/ch9.h> >> #include <linux/usb/gadget.h> >> >> +#include "dwc3_otg.h" >> + >> /* Global constants */ >> #define DWC3_EP0_BOUNCE_SIZE 512 >> #define DWC3_ENDPOINTS_NUM 32 >> @@ -152,8 +154,9 @@ >> /* OTG Registers */ >> #define DWC3_OCFG 0xcc00 >> #define DWC3_OCTL 0xcc04 >> -#define DWC3_OEVTEN 0xcc08 >> -#define DWC3_OSTS 0xcc0C >> +#define DWC3_OEVT 0xcc08 >> +#define DWC3_OEVTEN 0xcc0c >> +#define DWC3_OSTS 0xcc10 >> >> /* Bit fields */ >> >> @@ -203,6 +206,9 @@ >> #define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> >> 13) >> #define DWC3_MAX_HIBER_SCRATCHBUFS 15 >> >> +/* Global HWPARAMS6 Register */ >> +#define DWC3_GHWPARAMS6_SRP_SUPPORT (1 << 10) >> + >> /* Device Configuration Register */ >> #define DWC3_DCFG_LPM_CAP (1 << 22) >> #define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) >> @@ -358,6 +364,37 @@ >> #define DWC3_DEPCMD_TYPE_BULK 2 >> #define DWC3_DEPCMD_TYPE_INTR 3 >> >> +/* OTG Events Register */ >> +#define DWC3_OEVT_DEVICEMODE (1 << 31) >> +#define DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT (1 << 24) >> +#define DWC3_OEVTEN_OTGADEVBHOSTENDEVNT (1 << 20) >> +#define DWC3_OEVTEN_OTGADEVHOSTEVNT (1 << 19) >> +#define DWC3_OEVTEN_OTGADEVHNPCHNGEVNT (1 << 18) >> +#define DWC3_OEVTEN_OTGADEVSRPDETEVNT (1 << 17) >> +#define DWC3_OEVTEN_OTGADEVSESSENDDETEVNT (1 << 16) >> +#define DWC3_OEVTEN_OTGBDEVBHOSTENDEVNT (1 << 11) >> +#define DWC3_OEVTEN_OTGBDEVHNPCHNGEVNT (1 << 10) >> +#define DWC3_OEVTEN_OTGBDEVSESSVLDDETEVNT (1 << 9) >> +#define DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT (1 << 8) >> + >> +/* OTG OSTS register */ >> +#define DWC3_OTG_OSTS_OTGSTATE_SHIFT (8) >> +#define DWC3_OTG_OSTS_OTGSTATE (0xF << >> DWC3_OTG_OSTS_OTGSTATE_SHIFT) >> +#define DWC3_OTG_OSTS_PERIPHERALSTATE (1 << 4) >> +#define DWC3_OTG_OSTS_XHCIPRTPOWER (1 << 3) >> +#define DWC3_OTG_OSTS_BSESVALID (1 << 2) >> +#define DWC3_OTG_OSTS_VBUSVALID (1 << 1) >> +#define DWC3_OTG_OSTS_CONIDSTS (1 << 0) >> + >> +/* OTG OSTS register */ >> +#define DWC3_OTG_OCTL_PERIMODE (1 << 6) >> +#define DWC3_OTG_OCTL_PRTPWRCTL (1 << 5) >> +#define DWC3_OTG_OCTL_HNPREQ (1 << 4) >> +#define DWC3_OTG_OCTL_SESREQ (1 << 3) >> +#define DWC3_OTG_OCTL_TERMSELDLPULSE (1 << 2) >> +#define DWC3_OTG_OCTL_DEVSETHNPEN (1 << 1) >> +#define DWC3_OTG_OCTL_HSTSETHNPEN (1 << 0) >> + >> /* Structures */ >> >> struct dwc3_trb; >> @@ -658,6 +695,7 @@ struct dwc3 { >> spinlock_t lock; >> struct device *dev; >> >> + struct dwc3_otg *dotg; > > Please add description for new structure fields above. > >> struct platform_device *xhci; >> struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM]; >> >> @@ -720,6 +758,12 @@ struct dwc3 { >> >> u8 test_mode; >> u8 test_mode_nr; >> + >> + /* Indicate if the gadget was powered by the otg driver */ >> + bool vbus_active; >> + >> + /* Indicate if software connect was issued by the usb_gadget_driver >> */ >> + bool softconnect; >> }; >> >> /* >> ---------------------------------------------------------------------- >> ---- */ >> @@ -859,6 +903,9 @@ union dwc3_event { >> void dwc3_set_mode(struct dwc3 *dwc, u32 mode); >> int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc); >> >> +int dwc3_otg_init(struct dwc3 *dwc); >> +void dwc3_otg_exit(struct dwc3 *dwc); >> + >> int dwc3_host_init(struct dwc3 *dwc); >> void dwc3_host_exit(struct dwc3 *dwc); >> >> diff --git a/drivers/usb/dwc3/dwc3_otg.c b/drivers/usb/dwc3/dwc3_otg.c >> new file mode 100644 >> index 0000000..f0e8317 >> --- /dev/null >> +++ b/drivers/usb/dwc3/dwc3_otg.c >> @@ -0,0 +1,512 @@ >> +/** >> + * dwc3_otg.c - DesignWare USB3 DRD Controller OTG >> + * >> + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. >> + * >> + * This program is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License version 2 and >> + * only version 2 as published by the Free Software Foundation. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + */ >> + >> +#include <linux/usb.h> >> +#include <linux/usb/hcd.h> >> +#include <linux/platform_device.h> >> + >> +#include "core.h" >> +#include "dwc3_otg.h" >> +#include "io.h" >> +#include "xhci.h" >> + >> + >> +/** >> + * dwc3_otg_set_host_regs - reset dwc3 otg registers to host operation. >> + * >> + * This function sets the OTG registers to work in A-Device host mode. >> + * This function should be called just before entering to A-Device >> mode. >> + * >> + * @w: Pointer to the dwc3 otg workqueue. >> + */ >> +static void dwc3_otg_set_host_regs(struct dwc3_otg *dotg) >> +{ >> + u32 octl; >> + >> + /* Set OCTL[6](PeriMode) to 0 (host) */ >> + octl = dwc3_readl(dotg->regs, DWC3_OCTL); >> + octl &= ~DWC3_OTG_OCTL_PERIMODE; >> + dwc3_writel(dotg->regs, DWC3_OCTL, octl); >> + >> + /* >> + * TODO: add more OTG registers writes for HOST mode here, >> + * see figure 12-10 A-device flow in dwc3 Synopsis spec >> + */ >> +} >> + >> +/** >> + * dwc3_otg_set_peripheral_regs - reset dwc3 otg registers to >> peripheral >> operation. >> + * >> + * This function sets the OTG registers to work in B-Device peripheral >> mode. >> + * This function should be called just before entering to B-Device >> mode. >> + * >> + * @w: Pointer to the dwc3 otg workqueue. >> + */ >> +static void dwc3_otg_set_peripheral_regs(struct dwc3_otg *dotg) >> +{ >> + u32 octl; >> + >> + /* Set OCTL[6](PeriMode) to 1 (peripheral) */ >> + octl = dwc3_readl(dotg->regs, DWC3_OCTL); >> + octl |= DWC3_OTG_OCTL_PERIMODE; >> + dwc3_writel(dotg->regs, DWC3_OCTL, octl); >> + >> + /* >> + * TODO: add more OTG registers writes for PERIPHERAL mode here, >> + * see figure 12-19 B-device flow in dwc3 Synopsis spec >> + */ >> +} >> + >> +/** >> + * dwc3_otg_start_host - helper function for starting/stoping the host >> controller driver. >> + * >> + * @otg: Pointer to the otg_transceiver structure. >> + * @on: start / stop the host controller driver. >> + * >> + * Returns 0 on success otherwise negative errno. >> + */ >> +static int dwc3_otg_start_host(struct usb_otg *otg, int on) >> +{ >> + struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg); >> + struct usb_hcd *hcd; >> + struct xhci_hcd *xhci; >> + int ret = 0; >> + >> + if (!otg->host) >> + return -EINVAL; >> + >> + hcd = bus_to_hcd(otg->host); >> + xhci = hcd_to_xhci(hcd); >> + if (on) { >> + dev_dbg(otg->phy->dev, "%s: turn on host %s\n", >> + __func__, otg->host->bus_name); >> + dwc3_otg_set_host_regs(dotg); >> + >> + /* >> + * This should be revisited for more testing post-silicon. >> + * In worst case we may need to disconnect the root hub >> + * before stopping the controller so that it does not >> + * interfere with runtime pm/system pm. >> + * We can also consider registering and unregistering xhci >> + * platform device. It is almost similar to add_hcd and >> + * remove_hcd, But we may not use standard set_host method >> + * anymore. >> + */ >> + ret = hcd->driver->start(hcd); >> + if (ret) { >> + dev_err(otg->phy->dev, >> + "%s: failed to start primary hcd, ret=%d\n", >> + __func__, ret); >> + return ret; >> + } >> + >> + ret = xhci->shared_hcd->driver->start(xhci->shared_hcd); >> + if (ret) { >> + dev_err(otg->phy->dev, >> + "%s: failed to start secondary hcd, > ret=%d\n", >> + __func__, ret); >> + return ret; >> + } >> + } else { >> + dev_dbg(otg->phy->dev, "%s: turn off host %s\n", >> + __func__, otg->host->bus_name); >> + hcd->driver->stop(hcd); >> + } >> + >> + return 0; >> +} >> + >> +/** >> + * dwc3_otg_set_host - bind/unbind the host controller driver. >> + * >> + * @otg: Pointer to the otg_transceiver structure. >> + * @host: Pointer to the usb_bus structure. >> + * >> + * Returns 0 on success otherwise negative errno. >> + */ >> +static int dwc3_otg_set_host(struct usb_otg *otg, struct usb_bus *host) >> +{ >> + struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg); >> + >> + if (host) { >> + dev_dbg(otg->phy->dev, "%s: set host %s\n", >> + __func__, host->bus_name); >> + otg->host = host; >> + >> + /* >> + * Only after both peripheral and host are set then check >> + * OTG sm. This prevents unnecessary activation of the sm >> + * in case the ID is high. >> + */ >> + if (otg->gadget) >> + schedule_work(&dotg->sm_work); >> + } else { >> + if (otg->phy->state == OTG_STATE_A_HOST) { >> + dwc3_otg_start_host(otg, 0); >> + otg->host = NULL; >> + otg->phy->state = OTG_STATE_UNDEFINED; >> + schedule_work(&dotg->sm_work); >> + } else { >> + otg->host = NULL; >> + } >> + } >> + >> + return 0; >> +} >> + >> +/** >> + * dwc3_otg_start_peripheral - bind/unbind the peripheral controller. >> + * >> + * @otg: Pointer to the otg_transceiver structure. >> + * @gadget: pointer to the usb_gadget structure. >> + * >> + * Returns 0 on success otherwise negative errno. >> + */ >> +static int dwc3_otg_start_peripheral(struct usb_otg *otg, int on) >> +{ >> + struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg); >> + >> + if (!otg->gadget) >> + return -EINVAL; >> + >> + if (on) { >> + dev_dbg(otg->phy->dev, "%s: turn on gadget %s\n", >> + __func__, otg->gadget->name); >> + dwc3_otg_set_peripheral_regs(dotg); >> + usb_gadget_vbus_connect(otg->gadget); >> + } else { >> + dev_dbg(otg->phy->dev, "%s: turn off gadget %s\n", >> + __func__, otg->gadget->name); >> + usb_gadget_vbus_disconnect(otg->gadget); >> + } >> + >> + return 0; >> +} >> + >> +/** >> + * dwc3_otg_set_peripheral - bind/unbind the peripheral controller >> driver. >> + * >> + * @otg: Pointer to the otg_transceiver structure. >> + * @gadget: pointer to the usb_gadget structure. >> + * >> + * Returns 0 on success otherwise negative errno. >> + */ >> +static int dwc3_otg_set_peripheral(struct usb_otg *otg, >> + struct usb_gadget *gadget) >> +{ >> + struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg); >> + >> + if (gadget) { >> + dev_dbg(otg->phy->dev, "%s: set gadget %s\n", >> + __func__, gadget->name); >> + otg->gadget = gadget; >> + >> + /* >> + * Only after both peripheral and host are set then check >> + * OTG sm. This prevents unnecessary activation of the sm >> + * in case the ID is grounded. >> + */ >> + if (otg->host) >> + schedule_work(&dotg->sm_work); >> + } else { >> + if (otg->phy->state == OTG_STATE_B_PERIPHERAL) { >> + dwc3_otg_start_peripheral(otg, 0); >> + otg->gadget = NULL; >> + otg->phy->state = OTG_STATE_UNDEFINED; >> + schedule_work(&dotg->sm_work); >> + } else { >> + otg->gadget = NULL; >> + } >> + } >> + >> + return 0; >> +} >> + >> +/** >> + * dwc3_otg_interrupt - interrupt handler for dwc3 otg events. >> + * @_dotg: Pointer to out controller context structure >> + * >> + * Returns IRQ_HANDLED on success otherwise IRQ_NONE. >> + */ >> +static irqreturn_t dwc3_otg_interrupt(int irq, void *_dotg) >> +{ >> + struct dwc3_otg *dotg = (struct dwc3_otg *)_dotg; >> + u32 oevt_reg; >> + int ret = IRQ_NONE; >> + int handled_irqs = 0; > > Why don't you use u32 for handled_irqs? > >> + >> + oevt_reg = dwc3_readl(dotg->regs, DWC3_OEVT); >> + >> + if (oevt_reg & DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT) { >> + /* >> + * ID sts has changed, read it and later, in the workqueue >> + * function, switch from A to B or from B to A. >> + */ >> + dotg->osts = dwc3_readl(dotg->regs, DWC3_OSTS); >> + if ((dotg->otg.phy->state == OTG_STATE_B_IDLE) || >> + (dotg->otg.phy->state == OTG_STATE_A_IDLE)) { >> + >> + /* >> + * OTG state is ABOUT to change to A or B device, > but >> + * since ID sts was chnaged, then we return the > state > > Typo: chnaged -> changed > >> + * machine to the start point. >> + */ >> + dotg->otg.phy->state = OTG_STATE_UNDEFINED; >> + } >> + schedule_work(&dotg->sm_work); >> + >> + handled_irqs |= DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT; >> + ret = IRQ_HANDLED; >> + } >> + >> + /* >> + * Clear the interrupts we handled. >> + */ >> + if (ret == IRQ_HANDLED) >> + dwc3_writel(dotg->regs, DWC3_OEVT, handled_irqs); >> + >> + return ret; >> +} >> + >> +/** >> + * dwc3_otg_sm_work - workqueue function. >> + * >> + * @w: Pointer to the dwc3 otg workqueue >> + * >> + * NOTE: After any change in phy->state, >> + * we must reschdule the state machine. >> + */ >> +static void dwc3_otg_sm_work(struct work_struct *w) >> +{ >> + struct dwc3_otg *dotg = container_of(w, struct dwc3_otg, sm_work); >> + struct usb_phy *phy = dotg->otg.phy; >> + >> + dev_dbg(phy->dev, "%s state\n", otg_state_string(phy->state)); >> + >> + /* Check OTG state */ >> + switch (phy->state) { >> + case OTG_STATE_UNDEFINED: >> + /* Switch to A or B-Device according to IDSTS */ >> + if (dotg->osts & DWC3_OTG_OSTS_CONIDSTS) >> + phy->state = OTG_STATE_B_IDLE; >> + else >> + phy->state = OTG_STATE_A_IDLE; >> + >> + schedule_work(&dotg->sm_work); >> + break; >> + case OTG_STATE_B_IDLE: >> + if (dwc3_otg_start_peripheral(&dotg->otg, 1)) { >> + /* >> + * Probably set_peripheral was not called yet. >> + * We will re-try as soon as it will be called >> + */ >> + dev_err(phy->dev, >> + "unable to start B-device\n"); >> + phy->state = OTG_STATE_UNDEFINED; >> + } else >> + phy->state = OTG_STATE_B_PERIPHERAL; >> + >> + schedule_work(&dotg->sm_work); >> + break; >> + case OTG_STATE_B_PERIPHERAL: >> + if (!(dotg->osts & DWC3_OTG_OSTS_CONIDSTS)) { >> + dwc3_otg_start_peripheral(&dotg->otg, 0); >> + phy->state = OTG_STATE_A_IDLE; >> + schedule_work(&dotg->sm_work); >> + } >> + break; >> + case OTG_STATE_A_IDLE: >> + /* Switch to A-Device*/ >> + if (dwc3_otg_start_host(&dotg->otg, 1)) { >> + /* >> + * Probably set_host was not called yet. >> + * We will re-try as soon as it will be called >> + */ >> + dev_err(phy->dev, >> + "unable to start A-device\n"); >> + phy->state = OTG_STATE_UNDEFINED; >> + } else >> + phy->state = OTG_STATE_A_HOST; >> + >> + schedule_work(&dotg->sm_work); >> + break; >> + case OTG_STATE_A_HOST: >> + if (dotg->osts & DWC3_OTG_OSTS_CONIDSTS) { >> + dwc3_otg_start_host(&dotg->otg, 0); >> + phy->state = OTG_STATE_B_IDLE; >> + schedule_work(&dotg->sm_work); >> + } >> + break; >> + default: >> + dev_err(phy->dev, "%s: invalid otg-state\n", __func__); >> + >> + } >> +} >> + >> + >> +/** >> + * dwc3_otg_reset - reset dwc3 otg registers. >> + * >> + * @w: Pointer to the dwc3 otg workqueue >> + */ >> +static void dwc3_otg_reset(struct dwc3_otg *dotg) >> +{ >> + /* >> + * OCFG[2] - OTG-Version = 1 > > Data book says: OTG-Version > This is a debug bit and it should always be set to 1¡¯b0. (zero, not one) > >> + * OCFG[1] - HNPCap = 0 >> + * OCFG[0] - SRPCap = 0 >> + */ >> + dwc3_writel(dotg->regs, DWC3_OCFG, 0x4); >> + >> + /* >> + * OCTL[6] - PeriMode = 1 >> + * OCTL[5] - PrtPwrCtl = 0 >> + * OCTL[4] - HNPReq = 0 >> + * OCTL[3] - SesReq = 0 >> + * OCTL[2] - TermSelDLPulse = 0 >> + * OCTL[1] - DevSetHNPEn = 0 >> + * OCTL[0] - HstSetHNPEn = 0 >> + */ >> + dwc3_writel(dotg->regs, DWC3_OCTL, 0x40); >> + >> + /* Clear all otg events (interrupts) indications */ >> + dwc3_writel(dotg->regs, DWC3_OEVT, 0xFFFF); > > Did you want to clear bits 0..24 (0x1FFFFFF)? > >> + >> + /* Enable only the ConIDStsChngEn event*/ >> + dwc3_writel(dotg->regs, DWC3_OEVTEN, >> + DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT); >> + >> + /* Read OSTS */ >> + dotg->osts = dwc3_readl(dotg->regs, DWC3_OSTS); >> + >> +} >> + >> +/** >> + * dwc3_otg_init - Initializes otg related registers >> + * @dwc: Pointer to out controller context structure >> + * >> + * Returns 0 on success otherwise negative errno. >> + */ >> +int dwc3_otg_init(struct dwc3 *dwc) >> +{ >> + u32 reg; > > Please remove tab or use it for definitions below. > >> + int ret = 0; >> + struct dwc3_otg *dotg; >> + >> + dev_dbg(dwc->dev, "dwc3_otg_init\n"); > > How about dev_dbg(dwc->dev, "%s\n", __func__); > >> + >> + /* >> + * GHWPARAMS6[10] bit is SRPSupport. >> + * This bit also reflects DWC_USB3_EN_OTG >> + */ >> + reg = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6); >> + if (!(reg & DWC3_GHWPARAMS6_SRP_SUPPORT)) { >> + /* >> + * No OTG support in the HW core. >> + * We return 0 to indicate no error, since this is > acceptable >> + * situation, just continue probe the dwc3 driver without > otg. >> + */ >> + dev_dbg(dwc->dev, "dwc3_otg address space is not >> supported\n"); >> + return 0; >> + } >> + >> + /* Allocate and init otg instance */ >> + dotg = kzalloc(sizeof(struct dwc3_otg), GFP_KERNEL); >> + if (!dotg) { >> + dev_err(dwc->dev, "unable to allocate dwc3_otg\n"); >> + return -ENOMEM; >> + } >> + >> + dotg->irq = platform_get_irq(to_platform_device(dwc->dev), 0); >> + if (dotg->irq < 0) { >> + dev_err(dwc->dev, "%s: missing IRQ\n", __func__); >> + ret = -ENODEV; >> + goto err1; >> + } >> + >> + dotg->regs = dwc->regs; >> + >> + dotg->otg.set_peripheral = dwc3_otg_set_peripheral; >> + dotg->otg.set_host = dwc3_otg_set_host; >> + >> + /* This reference is used by dwc3 modules for checking otg >> existance */ >> + dwc->dotg = dotg; >> + >> + dotg->otg.phy = kzalloc(sizeof(struct usb_phy), GFP_KERNEL); >> + if (!dotg->otg.phy) { >> + dev_err(dwc->dev, "unable to allocate dwc3_otg.phy\n"); >> + ret = -ENOMEM; >> + goto err1; >> + } >> + >> + dotg->otg.phy->otg = &dotg->otg; >> + dotg->otg.phy->dev = dwc->dev; >> + >> + ret = usb_add_phy(dotg->otg.phy, USB_PHY_TYPE_USB3); >> + if (ret) { >> + dev_err(dotg->otg.phy->dev, >> + "%s: failed to set transceiver, already exists\n", >> + __func__); >> + goto err2; >> + } >> + >> + dwc3_otg_reset(dotg); >> + >> + dotg->otg.phy->state = OTG_STATE_UNDEFINED; >> + >> + INIT_WORK(&dotg->sm_work, dwc3_otg_sm_work); >> + >> + ret = request_irq(dotg->irq, dwc3_otg_interrupt, IRQF_SHARED, >> + "dwc3_otg", dotg); >> + if (ret) { >> + dev_err(dotg->otg.phy->dev, "failed to request irq #%d -- >> > %d\n", >> + dotg->irq, ret); >> + goto err3; >> + } >> + >> + return 0; >> + >> +err3: >> + cancel_work_sync(&dotg->sm_work); >> + usb_remove_phy(dotg->otg.phy); >> +err2: >> + kfree(dotg->otg.phy); >> +err1: >> + dwc->dotg = NULL; >> + kfree(dotg); >> + >> + return ret; >> +} >> + >> +/** >> + * dwc3_otg_exit >> + * @dwc: Pointer to out controller context structure >> + * >> + * Returns 0 on success otherwise negative errno. >> + */ >> +void dwc3_otg_exit(struct dwc3 *dwc) >> +{ >> + struct dwc3_otg *dotg = dwc->dotg; >> + >> + /* dotg is null when GHWPARAMS6[10]=SRPSupport=0, see dwc3_otg_init >> */ >> + if (dotg) { >> + cancel_work_sync(&dotg->sm_work); >> + usb_remove_phy(dotg->otg.phy); >> + free_irq(dotg->irq, dotg); >> + kfree(dotg->otg.phy); >> + kfree(dotg); >> + dwc->dotg = NULL; >> + } >> +} >> diff --git a/drivers/usb/dwc3/dwc3_otg.h b/drivers/usb/dwc3/dwc3_otg.h >> new file mode 100644 >> index 0000000..a048306 >> --- /dev/null >> +++ b/drivers/usb/dwc3/dwc3_otg.h >> @@ -0,0 +1,38 @@ >> +/** >> + * dwc3_otg.h - DesignWare USB3 DRD Controller OTG >> + * >> + * Copyright (c) 2012, Code Aurora Forum. All rights reserved. >> + * >> + * This program is free software; you can redistribute it and/or modify >> + * it under the terms of the GNU General Public License version 2 and >> + * only version 2 as published by the Free Software Foundation. >> + * >> + * This program is distributed in the hope that it will be useful, >> + * but WITHOUT ANY WARRANTY; without even the implied warranty of >> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the >> + * GNU General Public License for more details. >> + */ >> + >> +#ifndef __LINUX_USB_DWC3_OTG_H >> +#define __LINUX_USB_DWC3_OTG_H >> + >> +#include <linux/workqueue.h> >> + >> +#include <linux/usb/otg.h> >> + >> +/** >> + * struct dwc3_otg: OTG driver data. Shared by HCD and DCD. >> + * @otg: USB OTG Transceiver structure. >> + * @irq: IRQ number assigned for HSUSB controller. >> + * @regs: ioremapped register base address. >> + * @sm_work: OTG state machine work. >> + * @osts: last value of the OSTS register, as read from HW. >> + */ >> +struct dwc3_otg { >> + struct usb_otg otg; >> + int irq; >> + void __iomem *regs; >> + struct work_struct sm_work; >> + u32 osts; >> +}; >> +#endif /* __LINUX_USB_DWC3_OTG_H */ >> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c >> index 58fdfad..7d51411 100644 >> --- a/drivers/usb/dwc3/gadget.c >> +++ b/drivers/usb/dwc3/gadget.c >> @@ -49,6 +49,7 @@ >> >> #include <linux/usb/ch9.h> >> #include <linux/usb/gadget.h> >> +#include <linux/usb/otg.h> >> >> #include "core.h" >> #include "gadget.h" >> @@ -1462,12 +1463,64 @@ static int dwc3_gadget_pullup(struct usb_gadget > *g, >> int is_on) >> is_on = !!is_on; >> >> spin_lock_irqsave(&dwc->lock, flags); >> + >> + dwc->softconnect = is_on; >> + >> + if ((dwc->dotg && !dwc->vbus_active) || >> + !dwc->gadget_driver) { >> + >> + spin_unlock_irqrestore(&dwc->lock, flags); >> + >> + /* >> + * Need to wait for vbus_session(on) from otg driver or to >> + * the udc_start. >> + */ >> + return 0; >> + } >> + >> ret = dwc3_gadget_run_stop(dwc, is_on); >> + >> spin_unlock_irqrestore(&dwc->lock, flags); >> >> return ret; >> } >> >> +static int dwc3_gadget_vbus_session(struct usb_gadget *_gadget, int >> is_active) >> +{ >> + struct dwc3 *dwc = gadget_to_dwc(_gadget); >> + unsigned long flags; >> + >> + if (!dwc->dotg) >> + return -EPERM; >> + >> + is_active = !!is_active; >> + >> + spin_lock_irqsave(&dwc->lock, flags); >> + >> + /* Mark that the vbus was powered */ >> + dwc->vbus_active = is_active; >> + >> + /* >> + * Check if upper level usb_gadget_driver was already registerd >> with >> + * this udc controller driver (if dwc3_gadget_start was called) >> + */ >> + if (dwc->gadget_driver && dwc->softconnect) { >> + if (dwc->vbus_active) { >> + /* >> + * Both vbus was activated by otg and pullup was >> + * signaled by the gadget driver. >> + */ >> + dwc3_gadget_run_stop(dwc, 1); >> + } else { >> + dwc3_gadget_run_stop(dwc, 0); >> + } >> + } >> + >> + spin_unlock_irqrestore(&dwc->lock, flags); >> + >> + return 0; >> +} >> + >> static int dwc3_gadget_start(struct usb_gadget *g, >> struct usb_gadget_driver *driver) >> { >> @@ -1571,6 +1624,7 @@ static const struct usb_gadget_ops dwc3_gadget_ops >> = >> { >> .get_frame = dwc3_gadget_get_frame, >> .wakeup = dwc3_gadget_wakeup, >> .set_selfpowered = dwc3_gadget_set_selfpowered, >> + .vbus_session = dwc3_gadget_vbus_session, >> .pullup = dwc3_gadget_pullup, >> .udc_start = dwc3_gadget_start, >> .udc_stop = dwc3_gadget_stop, >> @@ -2471,6 +2525,15 @@ int __devinit dwc3_gadget_init(struct dwc3 *dwc) >> goto err7; >> } >> >> + if (dwc->dotg) { >> + /* dwc3 otg driver is active (DRD mode + SRPSupport=1) */ >> + ret = otg_set_peripheral(&dwc->dotg->otg, &dwc->gadget); >> + if (ret) { >> + dev_err(dwc->dev, "failed to set peripheral to > otg\n"); >> + goto err7; >> + } >> + } >> + >> return 0; >> >> err7: >> diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c >> index 689bc18..ef3f095 100644 >> --- a/drivers/usb/host/xhci-plat.c >> +++ b/drivers/usb/host/xhci-plat.c >> @@ -14,9 +14,12 @@ >> #include <linux/platform_device.h> >> #include <linux/module.h> >> #include <linux/slab.h> >> +#include <linux/usb/otg.h> >> >> #include "xhci.h" >> >> +static struct usb_phy *phy; >> + >> static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) >> { >> /* >> @@ -149,6 +152,19 @@ static int xhci_plat_probe(struct platform_device >> *pdev) >> if (ret) >> goto put_usb3_hcd; >> >> + phy = usb_get_phy(USB_PHY_TYPE_USB3); >> + if (phy && phy->otg) { >> + dev_dbg(&pdev->dev, "%s otg support available\n", __func__); >> + hcd->driver->stop(hcd); >> + ret = otg_set_host(phy->otg, &hcd->self); >> + if (ret) { >> + dev_err(&pdev->dev, "%s otg_set_host failed\n", >> + __func__); >> + usb_put_phy(phy); >> + goto put_usb3_hcd; >> + } >> + } >> + >> return 0; >> >> put_usb3_hcd: >> @@ -182,6 +198,11 @@ static int xhci_plat_remove(struct platform_device >> *dev) >> usb_put_hcd(hcd); >> kfree(xhci); >> >> + if (phy && phy->otg) { >> + otg_set_host(phy->otg, NULL); >> + usb_put_phy(phy); >> + } >> + >> return 0; >> } >> >> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c >> index a979cd0..287cecd 100644 >> --- a/drivers/usb/host/xhci.c >> +++ b/drivers/usb/host/xhci.c >> @@ -521,6 +521,13 @@ int xhci_run(struct usb_hcd *hcd) >> >> xhci_dbg(xhci, "xhci_run\n"); >> >> + xhci_dbg(xhci, "Calling HCD init\n"); >> + /* Initialize HCD and host controller data structures. */ >> + ret = xhci_init(hcd); >> + if (ret) >> + return ret; >> + xhci_dbg(xhci, "Called HCD init\n"); >> + >> ret = xhci_try_enable_msi(hcd); >> if (ret) >> return ret; >> @@ -4519,12 +4526,6 @@ int xhci_gen_setup(struct usb_hcd *hcd, >> xhci_get_quirks_t get_quirks) >> dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32)); >> } >> >> - xhci_dbg(xhci, "Calling HCD init\n"); >> - /* Initialize HCD and host controller data structures. */ >> - retval = xhci_init(hcd); >> - if (retval) >> - goto error; >> - xhci_dbg(xhci, "Called HCD init\n"); >> return 0; >> error: >> kfree(xhci); >> -- >> 1.7.6 >> >> Consultant for Qualcomm Innovation Center, Inc. >> Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum >> -- >> To unsubscribe from this list: send the line "unsubscribe linux-usb" in >> the body of a message to majordomo@xxxxxxxxxxxxxxx >> More majordomo info at http://vger.kernel.org/majordomo-info.html > > Thanks, I will gladly send update version according to your comments, Ido -- Consultant for Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html