Hi Anton,

On Tue, July 17, 2012 9:27 pm, Anton Tikhomirov wrote:
> Hi,
>
>> -----Original Message-----
>> From: linux-usb-ow...@vger.kernel.org [mailto:linux-usb-
>> ow...@vger.kernel.org] On Behalf Of Ido Shayevitz
>> Sent: Thursday, July 12, 2012 1:24 AM
>> To: ba...@ti.com
>> Cc: linux-usb@vger.kernel.org; i...@codeaurora.org
>> 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 <i...@codeaurora.org>
>>
>> ---
>>  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 majord...@vger.kernel.org
>> 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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to