Hi Ido,
> -----Original Message-----
> From: [email protected] [mailto:linux-usb-
> [email protected]] On Behalf Of Ido Shayevitz
> Sent: Wednesday, July 25, 2012 9:46 PM
> To: [email protected]; [email protected]
> Cc: [email protected]; [email protected]
> 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 <[email protected]>
>
> ---
> 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 [email protected]
> 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 [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html