Hi Ido, > -----Original Message----- > From: linux-usb-owner@xxxxxxxxxxxxxxx [mailto:linux-usb- > owner@xxxxxxxxxxxxxxx] On Behalf Of Ido Shayevitz > Sent: Wednesday, July 25, 2012 9:46 PM > To: balbi@xxxxxx; av.tikhomirov@xxxxxxxxxxx > Cc: linux-usb@xxxxxxxxxxxxxxx; idos@xxxxxxxxxxxxxx > Subject: [RFC/PATCH v3] 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 | 54 +++++- > 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, 711 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 c611d80..29a03e0 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; > @@ -611,6 +648,7 @@ struct dwc3_scratchpad_array { > * @ep0_bounce_addr: dma address of ep0_bounce > * @lock: for synchronizing > * @dev: pointer to our struct device > + * @dotg: pointer to the dwc3 otg instance > * @xhci: pointer to our xHCI child > * @event_buffer_list: a list of event buffers > * @gadget: device side representation of the peripheral controller > @@ -643,6 +681,8 @@ struct dwc3_scratchpad_array { > * @mem: points to start of memory which is used for this struct. > * @hwparams: copy of hwparams registers > * @root: debugfs root folder pointer > + * @vbus_active: Indicates if the gadget was powered by the otg driver > + * @softconnect: Indicates if pullup was issued by the usb_gadget_driver > */ > struct dwc3 { > struct usb_ctrlrequest *ctrl_req; > @@ -657,6 +697,7 @@ struct dwc3 { > spinlock_t lock; > struct device *dev; > > + struct dwc3_otg *dotg; > struct platform_device *xhci; > struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM]; > > @@ -719,6 +760,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 pullup was issued by the usb_gadget_driver */ > + bool softconnect; > }; > > /* ---------------------------------------------------------------------- > ---- */ > @@ -857,6 +904,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..b3bda11 > --- /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. This comment doesn't match the function definition (@dotg, not @w). > + */ > +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. This comment doesn't match the function definition (@dotg, not @w). > + */ > +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. This comment doesn't match the function definition (@on, not @gadget). > + * > + * 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. You forgot about @irq here. > + * @_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; > + u32 handled_irqs = 0; > + > + 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 changed, then we return the state > + * 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 'workqueue' term is confusing here. You use _default_ workqueue (has type 'struct workqueue_struct') and add work (pointed by @w) to this 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 This comment doesn't match the function definition (@dotg, not @w). > + */ > +static void dwc3_otg_reset(struct dwc3_otg *dotg) > +{ > + /* > + * OCFG[2] - OTG-Version = 0 > + * OCFG[1] - HNPCap = 0 > + * OCFG[0] - SRPCap = 0 > + */ > + dwc3_writel(dotg->regs, DWC3_OCFG, 0x0); > + > + /* > + * 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, 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; > + int ret = 0; > + struct dwc3_otg *dotg; > + > + 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 283c0cb..3c6ec7c 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" > @@ -1467,12 +1468,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) > { > @@ -1576,6 +1629,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, > @@ -2476,6 +2530,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 -- 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