[COMMON] usb: add USB driver for Exynos
authorKisang Lee <kisang80.lee@samsung.com>
Thu, 17 May 2018 11:59:31 +0000 (20:59 +0900)
committerKisang Lee <kisang80.lee@samsung.com>
Thu, 17 May 2018 12:22:41 +0000 (21:22 +0900)
Change-Id: Ifa547a52a4b88afd8f35c0af064e71c2706fb5f3
Signed-off-by: Kisang Lee <kisang80.lee@samsung.com>
34 files changed:
drivers/usb/common/common.c
drivers/usb/core/config.c
drivers/usb/core/hcd.c
drivers/usb/core/hub.c
drivers/usb/dwc3/Kconfig
drivers/usb/dwc3/Makefile
drivers/usb/dwc3/core.c
drivers/usb/dwc3/core.h
drivers/usb/dwc3/drd.c
drivers/usb/dwc3/dwc3-exynos.c
drivers/usb/dwc3/ep0.c
drivers/usb/dwc3/gadget.c
drivers/usb/dwc3/gadget.h
drivers/usb/dwc3/host.c
drivers/usb/gadget/Kconfig
drivers/usb/gadget/configfs.c
drivers/usb/gadget/function/Makefile
drivers/usb/gadget/function/f_fs.c
drivers/usb/gadget/function/f_rndis.c
drivers/usb/gadget/function/u_serial.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/ehci.h
drivers/usb/host/xhci-hub.c
drivers/usb/host/xhci-mem.c
drivers/usb/host/xhci-plat.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h
include/linux/usb.h
include/linux/usb/composite.h
include/linux/usb/exynos_usb_audio.h [new file with mode: 0644]
include/linux/usb/hcd.h
include/linux/usb/of.h
include/linux/usb/samsung_usb.h [new file with mode: 0644]

index 552ff7ac5a6b7a13820e3ba81d3667e81f43c2b2..fecfb673ca26c9db3a564134a7c7a160f0fe6b20 100644 (file)
@@ -126,6 +126,26 @@ enum usb_dr_mode usb_get_dr_mode(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(usb_get_dr_mode);
 
+/**
+ * of_usb_get_suspend_clk_freq - Get suspend clock frequency
+ *
+ * USB3 core needs 16KHz clock for a small part that operates
+ * when the SS PHY is in its lowest power (P3) state.
+ * USB3 core receives suspend clock and divides it to make 16KHz clock.
+ */
+unsigned int of_usb_get_suspend_clk_freq(struct device *dev)
+{
+       unsigned int freq;
+       int err;
+
+       err = device_property_read_u32(dev, "suspend_clk_freq", &freq);
+       if (err < 0)
+               return 0;
+
+       return freq;
+}
+EXPORT_SYMBOL_GPL(of_usb_get_suspend_clk_freq);
+
 #ifdef CONFIG_OF
 /**
  * of_usb_get_dr_mode_by_phy - Get dual role mode for the controller device
index 9e3355b9739618d7532e193a44022be41caa2bb0..528a4516c88c417cce0686007da2c815ffb2c711 100644 (file)
@@ -216,6 +216,16 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
        buffer += d->bLength;
        size -= d->bLength;
 
+       if (( d->bmAttributes & 0x3 ) == 0x1) {
+               if (d->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+                       to_usb_device(ddev)->hwinfo.in_ep = d->bEndpointAddress;
+                       dev_info(ddev, " This is IN ISO endpoint #0%x \n", d->bEndpointAddress);
+               } else {
+                       to_usb_device(ddev)->hwinfo.out_ep = d->bEndpointAddress;
+                       dev_info(ddev, " This is OUT ISO endpoint #0%x \n", d->bEndpointAddress);
+               }
+       }
+
        if (d->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE)
                n = USB_DT_ENDPOINT_AUDIO_SIZE;
        else if (d->bLength >= USB_DT_ENDPOINT_SIZE)
@@ -538,7 +548,7 @@ skip_to_next_interface_descriptor:
        return buffer - buffer0 + i;
 }
 
-static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
+int usb_parse_configuration(struct usb_device *dev, int cfgidx,
     struct usb_host_config *config, unsigned char *buffer, int size)
 {
        struct device *ddev = &dev->dev;
@@ -864,6 +874,12 @@ int usb_get_configuration(struct usb_device *dev)
                if (dev->quirks & USB_QUIRK_DELAY_INIT)
                        msleep(200);
 
+               dev->do_remote_wakeup =
+                       (desc->bmAttributes & USB_CONFIG_ATT_WAKEUP) ? 1 : 0;
+               if (dev->do_remote_wakeup == 1) {
+                       device_init_wakeup(ddev, 1);
+               }
+
                result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
                    bigbuffer, length);
                if (result < 0) {
@@ -879,6 +895,7 @@ int usb_get_configuration(struct usb_device *dev)
                }
 
                dev->rawdescriptors[cfgno] = bigbuffer;
+               dev->rawdesc_length = length;
 
                result = usb_parse_configuration(dev, cfgno,
                    &dev->config[cfgno], bigbuffer, length);
index 75ad6718858cad9d0c00023c4a273dd701a1d3a6..20d169150c8ded8ce3d177759672b06e06097e34 100644 (file)
@@ -2252,7 +2252,7 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
        int             status;
        int             old_state = hcd->state;
 
-       dev_dbg(&rhdev->dev, "bus %ssuspend, wakeup %d\n",
+       dev_info(&rhdev->dev, "bus %ssuspend, wakeup %d\n",
                        (PMSG_IS_AUTO(msg) ? "auto-" : ""),
                        rhdev->do_remote_wakeup);
        if (HCD_DEAD(hcd)) {
@@ -2292,6 +2292,13 @@ int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
                dev_dbg(&rhdev->dev, "bus %s fail, err %d\n",
                                "suspend", status);
        }
+
+       /* L2 suspend only support USB2.0port */
+       if ((hcd->state == HC_STATE_SUSPENDED) && !hcd->driver->hub_check_speed(hcd->shared_hcd)) {
+               dev_info(&rhdev->dev, "HCD STATE IS SUSPENDED, NEED WAKE UNLOCK\n");
+               hcd->driver->wake_lock(hcd, 0);
+       }
+
        return status;
 }
 
@@ -2301,7 +2308,7 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
        int             status;
        int             old_state = hcd->state;
 
-       dev_dbg(&rhdev->dev, "usb %sresume\n",
+       dev_info(&rhdev->dev, "usb %sresume\n",
                        (PMSG_IS_AUTO(msg) ? "auto-" : ""));
        if (HCD_DEAD(hcd)) {
                dev_dbg(&rhdev->dev, "skipped %s of dead bus\n", "resume");
@@ -2319,6 +2326,11 @@ int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
                struct usb_device *udev;
                int port1;
 
+               if (hcd->state == HC_STATE_RESUMING) {
+                       dev_info(&rhdev->dev, "HCD STATE IS RESUMING, NEED WAKE LOCK\n");
+                       hcd->driver->wake_lock(hcd, 1);
+               }
+
                spin_lock_irq(&hcd_root_hub_lock);
                if (!HCD_DEAD(hcd)) {
                        usb_set_device_state(rhdev, rhdev->actconfig
index 8f7d94239ee3e314780d9fe52ba8cf204ded3eaa..b6e365b32d60722d9e88ae53bdf7782f883ef3f3 100644 (file)
@@ -1685,6 +1685,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
        struct usb_host_interface *desc;
        struct usb_device *hdev;
        struct usb_hub *hub;
+       struct usb_hcd *hcd;
 
        desc = intf->cur_altsetting;
        hdev = interface_to_usbdev(intf);
@@ -1726,7 +1727,13 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
         *   usbcore.autosuspend = -1 then keep autosuspend disabled.
         */
 #ifdef CONFIG_PM
-       if (hdev->dev.power.autosuspend_delay >= 0)
+       /*
+        * bus suspend is executed before USB3.0 hub and devices are detected,
+        * which causes USB3.0 hub and devices aren't recognized when l2 sleep is enabled.
+        * Restoring suspend delay will resolve USB3.0 hub detect issue.
+        */
+       hcd = bus_to_hcd(hdev->bus);
+       if ((hdev->dev.power.autosuspend_delay >= 0) && !hcd->driver->usb_l2_check(hcd))
                pm_runtime_set_autosuspend_delay(&hdev->dev, 0);
 #endif
 
@@ -5133,6 +5140,7 @@ static void hub_event(struct work_struct *work)
        struct usb_interface *intf;
        struct usb_hub *hub;
        struct device *hub_dev;
+       struct usb_hcd *hcd;
        u16 hubstatus;
        u16 hubchange;
        int i, ret;
@@ -5140,6 +5148,7 @@ static void hub_event(struct work_struct *work)
        hub = container_of(work, struct usb_hub, events);
        hdev = hub->hdev;
        hub_dev = hub->intfdev;
+       hcd = bus_to_hcd(hdev->bus);
        intf = to_usb_interface(hub_dev);
 
        dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
@@ -5151,6 +5160,7 @@ static void hub_event(struct work_struct *work)
        /* Lock the device, then check to see if we were
         * disconnected while waiting for the lock to succeed. */
        usb_lock_device(hdev);
+       hcd->is_in_hub_event = true;
        if (unlikely(hub->disconnected))
                goto out_hdev_lock;
 
@@ -5243,6 +5253,7 @@ out_autopm:
        /* Balance the usb_autopm_get_interface() above */
        usb_autopm_put_interface_no_suspend(intf);
 out_hdev_lock:
+       hcd->is_in_hub_event = false;
        usb_unlock_device(hdev);
 
        /* Balance the stuff in kick_hub_wq() and allow autosuspend */
index ab8c0e0d3b60d54110c6a9a0daba4c10ef086eb1..4c9e56d8776a1f17b8c64cc811a82b1d5479381c 100644 (file)
@@ -41,7 +41,6 @@ config USB_DWC3_GADGET
 config USB_DWC3_DUAL_ROLE
        bool "Dual Role mode"
        depends on ((USB=y || USB=USB_DWC3) && (USB_GADGET=y || USB_GADGET=USB_DWC3))
-       depends on (EXTCON=y || EXTCON=USB_DWC3)
        help
          This is the default mode of working of DWC3 controller where
          both host and gadget features are enabled.
index 7ac725038f8d8e9ad09dc25d37484b58cf8457a1..d508dab4d14d3898f73689ddb66ff1a507618d13 100644 (file)
@@ -10,6 +10,8 @@ ifneq ($(CONFIG_FTRACE),)
        dwc3-y                          += trace.o
 endif
 
+dwc3-y                                 += otg.o
+
 ifneq ($(filter y,$(CONFIG_USB_DWC3_HOST) $(CONFIG_USB_DWC3_DUAL_ROLE)),)
        dwc3-y                          += host.o
 endif
index 3219d8157f5bfe4b28643559fcf10665d7bd97c4..1b033c7e2acd0dd9f4937e88b4904a8c465e77c8 100644 (file)
@@ -42,6 +42,7 @@
 #include <linux/usb/otg.h>
 
 #include "core.h"
+#include "otg.h"
 #include "gadget.h"
 #include "io.h"
 
@@ -53,6 +54,7 @@
  * dwc3_get_dr_mode - Validates and sets dr_mode
  * @dwc: pointer to our context structure
  */
+/* Remove warning - Check later!
 static int dwc3_get_dr_mode(struct dwc3 *dwc)
 {
        enum usb_dr_mode mode;
@@ -99,9 +101,10 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
 
        return 0;
 }
+*/
 
-static void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
-static int dwc3_event_buffers_setup(struct dwc3 *dwc);
+void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
+int dwc3_event_buffers_setup(struct dwc3 *dwc);
 
 static void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
 {
@@ -113,6 +116,7 @@ static void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
        dwc3_writel(dwc->regs, DWC3_GCTL, reg);
 }
 
+/*
 static void __dwc3_set_mode(struct work_struct *work)
 {
        struct dwc3 *dwc = work_to_dwc(work);
@@ -188,6 +192,141 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
 
        queue_work(system_freezable_wq, &dwc->drd_work);
 }
+*/
+
+void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
+{
+       u32 reg;
+
+       reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+       reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG));
+       reg |= DWC3_GCTL_PRTCAPDIR(mode);
+       dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+}
+
+void dwc3_core_config(struct dwc3 *dwc)
+{
+       u32 reg;
+
+       /* AHB bus configuration */
+       reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
+       reg |= (DWC3_GSBUSCFG0_INCRBRSTEN | DWC3_GSBUSCFG0_INCR16BRSTEN);
+       /**
+        * AXI Bus' cache type configuration for DMA transfer.
+        * By below setting, cache type was set to Cacheable/Modifiable.
+        * From DWC USB3.0 Link version 2.20A, this cache type could be set.
+        */
+       if (dwc->revision >= DWC3_REVISION_220A)
+               reg |= (DWC3_GSBUSCFG0_DESWRREQINFO |
+                       DWC3_GSBUSCFG0_DATWRREQINFO |
+                       DWC3_GSBUSCFG0_DESRDREQINFO |
+                       DWC3_GSBUSCFG0_DATRDREQINFO);
+       dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, reg);
+
+       reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG1);
+       reg |= (DWC3_GSBUSCFG1_BREQLIMIT(3));
+       dwc3_writel(dwc->regs, DWC3_GSBUSCFG1, reg);
+
+       /*
+        * WORKAROUND:
+        * For ss bulk-in data packet, when the host detects
+        * a DPP error or the internal buffer becomes full,
+        * it retries with an ACK TP Retry=1. Under the following
+        * conditions, the Retry=1 is falsely carried over to the next
+        * DWC3_GUCTL_USBHSTINAUTORETRYEN should be set to a one
+        * regardless of revision
+        * - There is only single active asynchronous SS EP at the time.
+        * - The active asynchronous EP is a Bulk IN EP.
+        * - The burst with the correctly Retry=1 ACK TP and
+        *   the next burst belong to the same transfer.
+        */
+       reg = dwc3_readl(dwc->regs, DWC3_GUCTL);
+       reg |= (DWC3_GUCTL_USBHSTINAUTORETRYEN);
+       if (dwc->adj_sof_accuracy) {
+               reg &= ~DWC3_GUCTL_REFCLKPER_MASK;
+               /* fix ITP interval time to 125us */
+               reg |= DWC3_GUCTL_REFCLKPER(0x14);
+       }
+       if (dwc->dis_u2_freeclk_exists_quirk) {
+               reg &= ~DWC3_GUCTL_REFCLKPER_MASK;
+               /* fix ITP interval time to 125us */
+               reg |= DWC3_GUCTL_REFCLKPER(0x14);
+       }
+       if (dwc->sparse_transfer_control)
+               reg |= DWC3_GUCTL_SPRSCTRLTRANSEN;
+       dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
+       if (dwc->revision >= DWC3_REVISION_190A &&
+               dwc->revision <= DWC3_REVISION_210A) {
+               reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
+               reg &= ~(DWC3_GRXTHRCFG_USBRXPKTCNT_MASK |
+                       DWC3_GRXTHRCFG_USBMAXRXBURSTSIZE_MASK);
+               reg |= (DWC3_GRXTHRCFG_USBRXPKTCNTSEL |
+                       DWC3_GRXTHRCFG_USBRXPKTCNT(3) |
+                       DWC3_GRXTHRCFG_USBMAXRXBURSTSIZE(3));
+               dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
+       }
+
+       /*
+        * WORKAROUND: DWC3 revisions 2.10a and earlier have a bug
+        * The delay of the entry to a low power state such that
+        * for applications where the link stays in a non-U0 state
+        * for a short duration(< 1 microsecond),
+        * the local PHY does not enter the low power state prior
+        * to receiving a potential LFPS wakeup.
+        * This causes the PHY CDR (Clock and Data Recovery) operation
+        * to be unstable for some Synopsys PHYs.
+        * The proposal now is to change the default and the recommended value
+        * for GUSB3PIPECTL[21:19] in the RTL from 3'b100 to a minimum of 3'b001
+        */
+       if (dwc->revision <= DWC3_REVISION_210A) {
+               reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+               reg &= ~(DWC3_GUSB3PIPECTL_DEP1P2P3_MASK);
+               reg |= (DWC3_GUSB3PIPECTL_DEP1P2P3_EN);
+               dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+       }
+
+       if (dwc->revision >= DWC3_REVISION_250A) {
+               reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+               reg |= DWC3_GUSB3PIPECTL_DISRXDETINP3;
+               dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+       }
+
+       if (dwc->revision >= DWC3_REVISION_250A) {
+               reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+               reg |= DWC3_GUSB3PIPECTL_DISRXDETINP3;
+               dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+       }
+
+       if (dwc->revision >= DWC3_REVISION_250A) {
+               reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+               reg |= DWC3_GUSB3PIPECTL_DISRXDETINP3;
+               dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+       }
+
+       if (dwc->revision >= DWC3_REVISION_250A) {
+               reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+               reg |= DWC3_GUSB3PIPECTL_DISRXDETINP3;
+               dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+       }
+
+       /*
+        * WORKAROUND: DWC3 revisions 2.10a and earlier have a bug
+        * Race Condition in PORTSC Write Followed by Read
+        * If the software quickly does a read to the PORTSC,
+        * some fields (port status change related fields
+        * like OCC, etc.) may not have correct value
+        * due to the current way of handling these bits.
+        * After clearing the status register (for example, OCC) bit
+        * by writing PORTSC tregister, software can insert some delay
+        * (for example, 5 mac2_clk -> UTMI clock = 60 MHz ->
+        * (16.66 ns x 5 = 84ns)) before reading the PORTSC to check status.
+        */
+       if (dwc->revision <= DWC3_REVISION_210A) {
+               reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+               reg |= (DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_16_BIT));
+               dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+       }
+}
 
 u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
 {
@@ -213,17 +352,23 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
        int             retries = 1000;
        int             ret;
 
+       ret = phy_power_on(dwc->usb3_generic_phy);
+       if (ret < 0)
+           return ret;
+
+       ret = phy_power_on(dwc->usb2_generic_phy);
+       if (ret < 0)
+           goto err_usb3phy_power;
+
        usb_phy_init(dwc->usb2_phy);
        usb_phy_init(dwc->usb3_phy);
        ret = phy_init(dwc->usb2_generic_phy);
        if (ret < 0)
-               return ret;
+               goto err_usb2phy_power;
 
        ret = phy_init(dwc->usb3_generic_phy);
-       if (ret < 0) {
-               phy_exit(dwc->usb2_generic_phy);
-               return ret;
-       }
+       if (ret < 0)
+               goto err_usb2phy_init;
 
        /*
         * We're resetting only the device side because, if we're in host mode,
@@ -246,6 +391,44 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
        } while (--retries);
 
        return -ETIMEDOUT;
+
+err_usb2phy_init:
+       phy_exit(dwc->usb2_generic_phy);
+
+err_usb2phy_power:
+       phy_power_off(dwc->usb2_generic_phy);
+
+err_usb3phy_power:
+       phy_power_off(dwc->usb3_generic_phy);
+
+       return ret;
+}
+
+/**
+ * dwc3_soft_reset - Issue soft reset
+ * @dwc: Pointer to our controller context structure
+ */
+int dwc3_soft_reset(struct dwc3 *dwc)
+{
+       unsigned long timeout;
+       u32 reg;
+
+       timeout = jiffies + msecs_to_jiffies(500);
+       dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
+       do {
+               reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+               if (!(reg & DWC3_DCTL_CSFTRST))
+                       break;
+
+               if (time_after(jiffies, timeout)) {
+                       dev_err(dwc->dev, "Reset Timed Out\n");
+                       return -ETIMEDOUT;
+               }
+
+               cpu_relax();
+       } while (true);
+
+       return 0;
 }
 
 /*
@@ -265,8 +448,10 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
 
        reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
        dft = reg & DWC3_GFLADJ_30MHZ_MASK;
-       if (!dev_WARN_ONCE(dwc->dev, dft == dwc->fladj,
-           "request value same as default, ignoring\n")) {
+       /*if (!dev_WARN_ONCE(dwc->dev, dft == dwc->fladj,
+        *    "request value same as default, ignoring\n")) {
+        */
+       if(dft != dwc->fladj) {
                reg &= ~DWC3_GFLADJ_30MHZ_MASK;
                reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj;
                dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
@@ -356,7 +541,7 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
  *
  * Returns 0 on success otherwise negative errno.
  */
-static int dwc3_event_buffers_setup(struct dwc3 *dwc)
+int dwc3_event_buffers_setup(struct dwc3 *dwc)
 {
        struct dwc3_event_buffer        *evt;
 
@@ -373,7 +558,7 @@ static int dwc3_event_buffers_setup(struct dwc3 *dwc)
        return 0;
 }
 
-static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
+void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
 {
        struct dwc3_event_buffer        *evt;
 
@@ -381,8 +566,6 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
 
        evt->lpos = 0;
 
-       dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0), 0);
-       dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0), 0);
        dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK
                        | DWC3_GEVNTSIZ_SIZE(0));
        dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0);
@@ -502,7 +685,7 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
  * initialized. The PHY interfaces and the PHYs get initialized together with
  * the core in dwc3_core_init.
  */
-static int dwc3_phy_setup(struct dwc3 *dwc)
+int dwc3_phy_setup(struct dwc3 *dwc)
 {
        u32 reg;
        int ret;
@@ -530,6 +713,9 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
        if (dwc->dis_rxdet_inp3_quirk)
                reg |= DWC3_GUSB3PIPECTL_DISRXDETINP3;
 
+       if (dwc->u1u2_exitfail_to_recov_quirk)
+               reg |= DWC3_GUSB3PIPECTL_U1U2EXITFAIL_TO_RECOV;
+
        if (dwc->req_p1p2p3_quirk)
                reg |= DWC3_GUSB3PIPECTL_REQP1P2P3;
 
@@ -619,12 +805,17 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
        if (dwc->dis_u2_freeclk_exists_quirk)
                reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
 
+       if (dwc->adj_sof_accuracy)
+               reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
+
+       reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
+
        dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
 
        return 0;
 }
 
-static void dwc3_core_exit(struct dwc3 *dwc)
+void dwc3_core_exit(struct dwc3 *dwc)
 {
        dwc3_event_buffers_cleanup(dwc);
 
@@ -637,6 +828,8 @@ static void dwc3_core_exit(struct dwc3 *dwc)
        usb_phy_set_suspend(dwc->usb3_phy, 1);
        phy_power_off(dwc->usb2_generic_phy);
        phy_power_off(dwc->usb3_generic_phy);
+
+       dwc->link_state = DWC3_LINK_STATE_SS_DIS;
 }
 
 static bool dwc3_core_is_valid(struct dwc3 *dwc)
@@ -731,6 +924,11 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
        if (dwc->revision < DWC3_REVISION_190A)
                reg |= DWC3_GCTL_U2RSTECN;
 
+       if (dwc->adj_sof_accuracy) {
+               reg &= ~DWC3_GCTL_SOFITPSYNC;
+               reg |= DWC3_GCTL_DSBLCLKGTNG;
+       }
+
        dwc3_writel(dwc->regs, DWC3_GCTL, reg);
 }
 
@@ -742,9 +940,10 @@ static int dwc3_core_get_phy(struct dwc3 *dwc);
  *
  * Returns 0 on success otherwise negative errno.
  */
-static int dwc3_core_init(struct dwc3 *dwc)
+int dwc3_core_init(struct dwc3 *dwc)
 {
        u32                     reg;
+       struct dwc3_otg         *dotg = dwc->dotg;
        int                     ret;
 
        if (!dwc3_core_is_valid(dwc)) {
@@ -770,6 +969,10 @@ static int dwc3_core_init(struct dwc3 *dwc)
        if (ret)
                goto err0;
 
+       /* Adjust SOF accuracy only for revisions >= 2.50a */
+       if (dwc->revision < DWC3_REVISION_250A)
+               dwc->adj_sof_accuracy = 0;
+
        ret = dwc3_core_soft_reset(dwc);
        if (ret)
                goto err0;
@@ -778,9 +981,27 @@ static int dwc3_core_init(struct dwc3 *dwc)
        if (ret)
                goto err0;
 
+       if (dotg) {
+               phy_tune(dwc->usb2_generic_phy, dotg->otg.state);
+               phy_tune(dwc->usb3_generic_phy, dotg->otg.state);
+       } else {
+               phy_tune(dwc->usb2_generic_phy, 0);
+               phy_tune(dwc->usb3_generic_phy, 0);
+       }
+
        dwc3_core_setup_global_control(dwc);
        dwc3_core_num_eps(dwc);
 
+       dwc->suspend_clk_freq = 50000000;
+       if (dwc->suspend_clk_freq) {
+               reg &= ~DWC3_GCTL_PWRDNSCALE_MASK;
+               reg |= DWC3_GCTL_PWRDNSCALE(dwc->suspend_clk_freq/(16*1000));
+       }
+
+       dev_info(dwc->dev, "%s: max speed:%d, hibernation:%d, nr_scratch:%d\n",
+                       __func__, dwc->maximum_speed, dwc->has_hibernation,
+                       dwc->nr_scratch);
+
        ret = dwc3_setup_scratch_buffers(dwc);
        if (ret)
                goto err1;
@@ -790,18 +1011,11 @@ static int dwc3_core_init(struct dwc3 *dwc)
 
        usb_phy_set_suspend(dwc->usb2_phy, 0);
        usb_phy_set_suspend(dwc->usb3_phy, 0);
-       ret = phy_power_on(dwc->usb2_generic_phy);
-       if (ret < 0)
-               goto err2;
-
-       ret = phy_power_on(dwc->usb3_generic_phy);
-       if (ret < 0)
-               goto err3;
 
        ret = dwc3_event_buffers_setup(dwc);
        if (ret) {
                dev_err(dwc->dev, "failed to setup event buffers\n");
-               goto err4;
+               goto err2;
        }
 
        /*
@@ -831,15 +1045,25 @@ static int dwc3_core_init(struct dwc3 *dwc)
                dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
        }
 
-       return 0;
+        dwc3_core_config(dwc);
 
-err4:
-       phy_power_off(dwc->usb3_generic_phy);
+       if (dwc->adj_sof_accuracy) {
+               reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
+               reg &= ~DWC3_GFLADJ_REFCLK_240MHZDECR_PLS1;
+               reg &= ~DWC3_GFLADJ_REFCLK_240MHZ_DECR_MASK;
+               reg |= DWC3_GFLADJ_REFCLK_240MHZ_DECR(0xA);
+               reg |= DWC3_GFLADJ_REFCLK_LPM_SEL;
+               reg &= ~DWC3_GFLADJ_REFCLK_FLADJ_MASK;
+               reg |= DWC3_GFLADJ_REFCLK_FLADJ(0x7F0);
+               dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
+       }
 
-err3:
-       phy_power_off(dwc->usb2_generic_phy);
+       dwc->link_state = DWC3_LINK_STATE_U0;
+       return 0;
 
 err2:
+       phy_power_off(dwc->usb3_generic_phy);
+       phy_power_off(dwc->usb2_generic_phy);
        usb_phy_set_suspend(dwc->usb2_phy, 1);
        usb_phy_set_suspend(dwc->usb3_phy, 1);
 
@@ -849,6 +1073,10 @@ err1:
        phy_exit(dwc->usb2_generic_phy);
        phy_exit(dwc->usb3_generic_phy);
 
+       phy_power_off(dwc->usb2_generic_phy);
+       phy_power_off(dwc->usb3_generic_phy);
+
+       dwc->link_state = DWC3_LINK_STATE_SS_DIS;
 err0:
        return ret;
 }
@@ -957,6 +1185,46 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
                }
                break;
        case USB_DR_MODE_OTG:
+               ret = dwc3_otg_init(dwc);
+               if (ret) {
+                       dev_err(dev, "failed to initialize otg\n");
+                       return ret;
+               }
+
+               /* turn off PHYs to save power */
+               dwc3_core_exit(dwc);
+
+               ret = dwc3_host_init(dwc);
+               if (ret) {
+                       if (ret != -EPROBE_DEFER)
+                               dev_err(dev, "failed to initialize host\n");
+                       dwc3_otg_exit(dwc);
+                       return ret;
+               }
+               ret = dwc3_gadget_init(dwc);
+               if (ret) {
+                       if (ret != -EPROBE_DEFER)
+                               dev_err(dev, "failed to initialize gadget\n");
+                       dwc3_host_exit(dwc);
+                       dwc3_otg_exit(dwc);
+                       return ret;
+               }
+
+               /* Now we are ready to start OTG */
+               ret = dwc3_otg_start(dwc);
+               if (ret) {
+                       dev_err(dev, "failed to start otg\n");
+                       dwc3_host_exit(dwc);
+                       dwc3_gadget_exit(dwc);
+                       dwc3_otg_exit(dwc);
+                       return ret;
+               }
+
+               /* Unblock runtime PM for OTG */
+               pm_runtime_put_sync(dev);
+
+               /* New drd routine in Kernel 4.14 */
+               /*
                INIT_WORK(&dwc->drd_work, __dwc3_set_mode);
                ret = dwc3_drd_init(dwc);
                if (ret) {
@@ -964,6 +1232,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
                                dev_err(dev, "failed to initialize dual-role\n");
                        return ret;
                }
+               */
                break;
        default:
                dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);
@@ -991,12 +1260,42 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
        }
 }
 
+static int dwc3_get_option(struct dwc3 *dwc)
+{
+       struct device           *dev = dwc->dev;
+       struct device_node      *node = dev->of_node;
+       int                     value;
+
+       if (!of_property_read_u32(node, "adj-sof-accuracy", &value)) {
+               dwc->adj_sof_accuracy = value ? true : false;
+       } else {
+               dev_err(dev, "can't get adj-sof-accuracy from %s node", node->name);
+               return -EINVAL;
+       }
+       if (!of_property_read_u32(node, "is_not_vbus_pad", &value)) {
+               dwc->is_not_vbus_pad = value ? true : false;
+       } else {
+               dev_err(dev, "can't get is_not_vbus_pad from %s node", node->name);
+               return -EINVAL;
+       }
+       if (!of_property_read_u32(node, "enable_sprs_transfer", &value)) {
+               dwc->sparse_transfer_control = value ? true : false;
+       } else {
+               dev_err(dev, "can't get sprs-xfer-ctrl from %s node", node->name);
+               return -EINVAL;
+       }
+
+       return 0;
+
+}
+
 static void dwc3_get_properties(struct dwc3 *dwc)
 {
        struct device           *dev = dwc->dev;
        u8                      lpm_nyet_threshold;
        u8                      tx_de_emphasis;
        u8                      hird_threshold;
+       int ret;
 
        /* default to highest possible threshold */
        lpm_nyet_threshold = 0xff;
@@ -1012,8 +1311,13 @@ static void dwc3_get_properties(struct dwc3 *dwc)
 
        dwc->maximum_speed = usb_get_maximum_speed(dev);
        dwc->dr_mode = usb_get_dr_mode(dev);
+       dwc->suspend_clk_freq = of_usb_get_suspend_clk_freq(dev);
        dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node);
 
+       ret = dwc3_get_option(dwc);
+       if (ret)
+               dev_err(dev, "dwc3_get_option error occurred\n");
+
        dwc->sysdev_is_parent = device_property_read_bool(dev,
                                "linux,sysdev_is_parent");
        if (dwc->sysdev_is_parent)
@@ -1042,6 +1346,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
                                "snps,req_p1p2p3_quirk");
        dwc->del_p1p2p3_quirk = device_property_read_bool(dev,
                                "snps,del_p1p2p3_quirk");
+       dwc->u1u2_exitfail_to_recov_quirk = device_property_read_bool(dev,
+                               "snps,u1u2_exitfail_quirk");
        dwc->del_phy_power_chg_quirk = device_property_read_bool(dev,
                                "snps,del_phy_power_chg_quirk");
        dwc->lfps_filter_quirk = device_property_read_bool(dev,
@@ -1072,6 +1378,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
        device_property_read_u32(dev, "snps,quirk-frame-length-adjustment",
                                 &dwc->fladj);
 
+       dev_info(dwc->dev, "%s: dr_mode:%d, suspend clock:%dMHz\n", __func__,
+                       dwc->dr_mode , dwc->suspend_clk_freq/1000000);
+
        dwc->lpm_nyet_threshold = lpm_nyet_threshold;
        dwc->tx_de_emphasis = tx_de_emphasis;
 
@@ -1149,6 +1458,7 @@ static int dwc3_probe(struct platform_device *pdev)
 
        void __iomem            *regs;
 
+       pr_info("%s: +++\n", __func__);
        dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
        if (!dwc)
                return -ENOMEM;
@@ -1184,20 +1494,17 @@ static int dwc3_probe(struct platform_device *pdev)
 
        dwc3_get_properties(dwc);
 
+       ret = dma_set_mask(dwc->sysdev, DMA_BIT_MASK(36));
+       if (ret) {
+               pr_err("dma set mask ret = %d\n", ret);
+               return ret;
+       }
+
        platform_set_drvdata(pdev, dwc);
        dwc3_cache_hwparams(dwc);
 
        spin_lock_init(&dwc->lock);
-
-       pm_runtime_set_active(dev);
-       pm_runtime_use_autosuspend(dev);
-       pm_runtime_set_autosuspend_delay(dev, DWC3_DEFAULT_AUTOSUSPEND_DELAY);
-       pm_runtime_enable(dev);
-       ret = pm_runtime_get_sync(dev);
-       if (ret < 0)
-               goto err1;
-
-       pm_runtime_forbid(dev);
+       init_completion(&dwc->disconnect);
 
        ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
        if (ret) {
@@ -1206,14 +1513,27 @@ static int dwc3_probe(struct platform_device *pdev)
                goto err2;
        }
 
-       ret = dwc3_get_dr_mode(dwc);
-       if (ret)
-               goto err3;
+       //ret = dwc3_get_dr_mode(dwc);
+       //if (ret)
+       //      goto err3;
 
        ret = dwc3_alloc_scratch_buffers(dwc);
        if (ret)
                goto err3;
 
+       /* pm_runtime_get_sync triggers dwc3_core_init, which causes Kernel panic
+               because dwc3_core_init is called in pm_runtime_get_sync before dwc3_alloc_event_buffers.
+               Move the location to call pm runtime api  */
+       pm_runtime_set_active(dev);
+       pm_runtime_use_autosuspend(dev);
+       pm_runtime_set_autosuspend_delay(dev, DWC3_DEFAULT_AUTOSUSPEND_DELAY);
+       pm_runtime_enable(dev);
+       ret = pm_runtime_get_sync(dev);
+       if (ret < 0)
+               goto err1;
+
+       pm_runtime_forbid(dev);
+
        ret = dwc3_core_init(dwc);
        if (ret) {
                dev_err(dev, "failed to initialize core\n");
@@ -1227,8 +1547,11 @@ static int dwc3_probe(struct platform_device *pdev)
                goto err5;
 
        dwc3_debugfs_init(dwc);
-       pm_runtime_put(dev);
+       ret = pm_runtime_put(dev);
+       pr_info("%s, pm_runtime_put = %d\n",
+                       __func__, ret);
 
+       pr_info("%s: ---\n", __func__);
        return 0;
 
 err5:
@@ -1278,7 +1601,8 @@ static int dwc3_remove(struct platform_device *pdev)
        dwc3_core_exit(dwc);
        dwc3_ulpi_exit(dwc);
 
-       pm_runtime_put_sync(&pdev->dev);
+       if (dwc->dr_mode != USB_DR_MODE_OTG)
+               pm_runtime_put_sync(&pdev->dev);
        pm_runtime_allow(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
@@ -1293,120 +1617,53 @@ static int dwc3_suspend_common(struct dwc3 *dwc)
 {
        unsigned long   flags;
 
+       /* bring to full power */
+       pm_runtime_get_sync(dwc->dev);
+
        switch (dwc->dr_mode) {
        case USB_DR_MODE_PERIPHERAL:
-       case USB_DR_MODE_OTG:
                spin_lock_irqsave(&dwc->lock, flags);
                dwc3_gadget_suspend(dwc);
                spin_unlock_irqrestore(&dwc->lock, flags);
                break;
+       case USB_DR_MODE_OTG:
+               dwc3_event_buffers_cleanup(dwc);
+               break;
        case USB_DR_MODE_HOST:
+               usb_phy_shutdown(dwc->usb3_phy);
+               usb_phy_shutdown(dwc->usb2_phy);
+               phy_exit(dwc->usb2_generic_phy);
+               phy_exit(dwc->usb3_generic_phy);
+
+               phy_power_off(dwc->usb2_generic_phy);
+               phy_power_off(dwc->usb3_generic_phy);
        default:
                /* do nothing */
                break;
        }
 
-       dwc3_core_exit(dwc);
-
        return 0;
 }
 
 static int dwc3_resume_common(struct dwc3 *dwc)
 {
        unsigned long   flags;
-       int             ret;
 
-       ret = dwc3_core_init(dwc);
-       if (ret)
-               return ret;
+       /* executing phy_init() twice caused 'ep0out' enable failure in resume.
+       *       don't call dwc3_core_init
+       * ret = dwc3_core_init(dwc);
+       */
+       dwc3_event_buffers_setup(dwc);
 
        switch (dwc->dr_mode) {
        case USB_DR_MODE_PERIPHERAL:
-       case USB_DR_MODE_OTG:
                spin_lock_irqsave(&dwc->lock, flags);
                dwc3_gadget_resume(dwc);
                spin_unlock_irqrestore(&dwc->lock, flags);
-               /* FALLTHROUGH */
-       case USB_DR_MODE_HOST:
-       default:
-               /* do nothing */
-               break;
-       }
-
-       return 0;
-}
-
-static int dwc3_runtime_checks(struct dwc3 *dwc)
-{
-       switch (dwc->dr_mode) {
-       case USB_DR_MODE_PERIPHERAL:
-       case USB_DR_MODE_OTG:
-               if (dwc->connected)
-                       return -EBUSY;
-               break;
-       case USB_DR_MODE_HOST:
-       default:
-               /* do nothing */
-               break;
-       }
-
-       return 0;
-}
-
-static int dwc3_runtime_suspend(struct device *dev)
-{
-       struct dwc3     *dwc = dev_get_drvdata(dev);
-       int             ret;
-
-       if (dwc3_runtime_checks(dwc))
-               return -EBUSY;
-
-       ret = dwc3_suspend_common(dwc);
-       if (ret)
-               return ret;
-
-       device_init_wakeup(dev, true);
-
-       return 0;
-}
-
-static int dwc3_runtime_resume(struct device *dev)
-{
-       struct dwc3     *dwc = dev_get_drvdata(dev);
-       int             ret;
-
-       device_init_wakeup(dev, false);
-
-       ret = dwc3_resume_common(dwc);
-       if (ret)
-               return ret;
-
-       switch (dwc->dr_mode) {
-       case USB_DR_MODE_PERIPHERAL:
-       case USB_DR_MODE_OTG:
-               dwc3_gadget_process_pending_events(dwc);
-               break;
-       case USB_DR_MODE_HOST:
-       default:
-               /* do nothing */
                break;
-       }
-
-       pm_runtime_mark_last_busy(dev);
-       pm_runtime_put(dev);
-
-       return 0;
-}
-
-static int dwc3_runtime_idle(struct device *dev)
-{
-       struct dwc3     *dwc = dev_get_drvdata(dev);
-
-       switch (dwc->dr_mode) {
-       case USB_DR_MODE_PERIPHERAL:
+       /* Applying KC OTG and Role switch driver patch  - e40c2ae,
+               it's difficult to apply the patch because dwc3_resume func was changed in Kernel4.9 */
        case USB_DR_MODE_OTG:
-               if (dwc3_runtime_checks(dwc))
-                       return -EBUSY;
                break;
        case USB_DR_MODE_HOST:
        default:
@@ -1414,9 +1671,6 @@ static int dwc3_runtime_idle(struct device *dev)
                break;
        }
 
-       pm_runtime_mark_last_busy(dev);
-       pm_runtime_autosuspend(dev);
-
        return 0;
 }
 #endif /* CONFIG_PM */
@@ -1427,6 +1681,7 @@ static int dwc3_suspend(struct device *dev)
        struct dwc3     *dwc = dev_get_drvdata(dev);
        int             ret;
 
+       pr_info("[%s]\n", __func__);
        ret = dwc3_suspend_common(dwc);
        if (ret)
                return ret;
@@ -1441,6 +1696,7 @@ static int dwc3_resume(struct device *dev)
        struct dwc3     *dwc = dev_get_drvdata(dev);
        int             ret;
 
+       pr_info("[%s]\n", __func__);
        pinctrl_pm_select_default_state(dev);
 
        ret = dwc3_resume_common(dwc);
@@ -1451,14 +1707,17 @@ static int dwc3_resume(struct device *dev)
        pm_runtime_set_active(dev);
        pm_runtime_enable(dev);
 
+       /* Compensate usage count incremented during prepare */
+       pm_runtime_put_sync(dev);
+
        return 0;
 }
 #endif /* CONFIG_PM_SLEEP */
 
 static const struct dev_pm_ops dwc3_dev_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
-       SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume,
-                       dwc3_runtime_idle)
+       /*SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume,
+                       dwc3_runtime_idle) */
 };
 
 #ifdef CONFIG_OF
index fc28819253f71acfaa043f7c0412ea059fb96f3e..7d6668f10491046d3e24d257977a8382074601a4 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/mm.h>
 #include <linux/debugfs.h>
+#include <linux/completion.h>
 #include <linux/wait.h>
 #include <linux/workqueue.h>
 
 /* Bit fields */
 
 /* Global Debug Queue/FIFO Space Available Register */
+#define DWC3_GSBUSCFG0_INCRBRSTEN      (1 << 0)
+#define DWC3_GSBUSCFG0_INCR4BRSTEN     (1 << 1)
+#define DWC3_GSBUSCFG0_INCR8BRSTEN     (1 << 2)
+#define DWC3_GSBUSCFG0_INCR16BRSTEN    (1 << 3)
+#define DWC3_GSBUSCFG0_INCR32BRSTEN    (1 << 4)
+#define DWC3_GSBUSCFG0_INCR64BRSTEN    (1 << 5)
+#define DWC3_GSBUSCFG0_INCR128BRSTEN   (1 << 6)
+#define DWC3_GSBUSCFG0_INCR256BRSTEN   (1 << 7)
+#define DWC3_GSBUSCFG0_DESWRREQINFO    (2 << 16)
+#define DWC3_GSBUSCFG0_DATWRREQINFO    (2 << 20)
+#define DWC3_GSBUSCFG0_DESRDREQINFO    (2 << 24)
+#define DWC3_GSBUSCFG0_DATRDREQINFO    (2 << 28)
 #define DWC3_GDBGFIFOSPACE_NUM(n)      ((n) & 0x1f)
 #define DWC3_GDBGFIFOSPACE_TYPE(n)     (((n) << 5) & 0x1e0)
 #define DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(n) (((n) >> 16) & 0xffff)
 
+#define DWC3_GSBUSCFG1_BREQLIMIT(n)    ((n) << 8)
+#define DWC3_GSBUSCFG1_BREQLIMIT_SHIFT 8
+#define DWC3_GSBUSCFG1_BREQLIMIT_MASK  (0xf << 8)
+#define DWC3_GSBUSCFG1_EN1KPAGE                (1 << 12)
+
+#define DWC3_GRXTHRCFG_USBRXPKTCNTSEL          (1 << 29)
+#define DWC3_GRXTHRCFG_USBRXPKTCNT_MASK                (0xf << 24)
+#define DWC3_GRXTHRCFG_USBRXPKTCNT_SHIFT       24
+#define DWC3_GRXTHRCFG_USBRXPKTCNT(n)          ((n) << 24)
+#define DWC3_GRXTHRCFG_USBMAXRXBURSTSIZE_MASK  (0x1f << 19)
+#define DWC3_GRXTHRCFG_USBMAXRXBURSTSIZE_SHIFT 19
+#define DWC3_GRXTHRCFG_USBMAXRXBURSTSIZE(n)    ((n) << 19)
+
 #define DWC3_TXFIFOQ           0
 #define DWC3_RXFIFOQ           1
 #define DWC3_TXREQQ            2
 
 /* Global Configuration Register */
 #define DWC3_GCTL_PWRDNSCALE(n)        ((n) << 19)
+#define DWC3_GCTL_PWRDNSCALE_MASK      DWC3_GCTL_PWRDNSCALE(0x1fff)
 #define DWC3_GCTL_U2RSTECN     BIT(16)
 #define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6)
 #define DWC3_GCTL_CLK_BUS      (0)
 #define DWC3_GCTL_GBLHIBERNATIONEN     BIT(1)
 #define DWC3_GCTL_DSBLCLKGTNG          BIT(0)
 
+#define DWC3_GUCTL_REFCLKPER(n)                ((n) << 22)
+#define DWC3_GUCTL_REFCLKPER_MASK      DWC3_GUCTL_REFCLKPER(0x3FF)
+#define DWC3_GUCTL_USBHSTINAUTORETRYEN (1 << 14)
+#define DWC3_GUCTL_SPRSCTRLTRANSEN     (1 << 17)
+
 /* Global User Control 1 Register */
 #define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS     BIT(28)
 #define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW  BIT(24)
 #define DWC3_GUSB3PIPECTL_U2SSINP3OK   BIT(29)
 #define DWC3_GUSB3PIPECTL_DISRXDETINP3 BIT(28)
 #define DWC3_GUSB3PIPECTL_UX_EXIT_PX   BIT(27)
+#define DWC3_GUSB3PIPECTL_U1U2EXITFAIL_TO_RECOV        BIT(25)
 #define DWC3_GUSB3PIPECTL_REQP1P2P3    BIT(24)
 #define DWC3_GUSB3PIPECTL_DEP1P2P3(n)  ((n) << 19)
 #define DWC3_GUSB3PIPECTL_DEP1P2P3_MASK        DWC3_GUSB3PIPECTL_DEP1P2P3(7)
 #define DWC3_GHWPARAMS7_RAM2_DEPTH(n)  (((n) >> 16) & 0xffff)
 
 /* Global Frame Length Adjustment Register */
+#define DWC3_GFLADJ_REFCLK_240MHZDECR_PLS1     BIT(31)
+#define DWC3_GFLADJ_REFCLK_240MHZ_DECR(n)      ((n) << 24)
+#define DWC3_GFLADJ_REFCLK_240MHZ_DECR_MASK    DWC3_GFLADJ_REFCLK_240MHZ_DECR(0x7F)
+#define DWC3_GFLADJ_REFCLK_LPM_SEL             BIT(23)
+#define DWC3_GFLADJ_REFCLK_FLADJ(n)            ((n) << 8)
+#define DWC3_GFLADJ_REFCLK_FLADJ_MASK          DWC3_GFLADJ_REFCLK_FLADJ(0x3FFF)
 #define DWC3_GFLADJ_30MHZ_SDBND_SEL            BIT(7)
 #define DWC3_GFLADJ_30MHZ_MASK                 0x3f
 
 #define DWC3_DCFG_SUPERSPEED_PLUS (5 << 0)  /* DWC_usb31 only */
 #define DWC3_DCFG_SUPERSPEED   (4 << 0)
 #define DWC3_DCFG_HIGHSPEED    (0 << 0)
-#define DWC3_DCFG_FULLSPEED    BIT(0)
+#define DWC3_DCFG_FULLSPEED    (1 << 0) // Check later!
 #define DWC3_DCFG_LOWSPEED     (2 << 0)
+#define DWC3_DCFG_FULLSPEED1   (3 << 0) // Check later!
 
 #define DWC3_DCFG_NUMP_SHIFT   17
 #define DWC3_DCFG_NUMP(n)      (((n) >> DWC3_DCFG_NUMP_SHIFT) & 0x1f)
 #define DWC3_DEVTEN_ERRTICERREN                BIT(9)
 #define DWC3_DEVTEN_SOFEN              BIT(7)
 #define DWC3_DEVTEN_EOPFEN             BIT(6)
+#define DWC3_DEVTEN_U3L2_SUSPEN        BIT(6)
 #define DWC3_DEVTEN_HIBERNATIONREQEVTEN        BIT(5)
 #define DWC3_DEVTEN_WKUPEVTEN          BIT(4)
 #define DWC3_DEVTEN_ULSTCNGEN          BIT(3)
 #define DWC3_DSTS_SUPERSPEED_PLUS      (5 << 0) /* DWC_usb31 only */
 #define DWC3_DSTS_SUPERSPEED           (4 << 0)
 #define DWC3_DSTS_HIGHSPEED            (0 << 0)
-#define DWC3_DSTS_FULLSPEED            BIT(0)
+#define DWC3_DSTS_FULLSPEED            (1 << 0) // Check later!
 #define DWC3_DSTS_LOWSPEED             (2 << 0)
+#define DWC3_DSTS_FULLSPEED1           (3 << 0)
+
 
 /* Device Generic Command Register */
 #define DWC3_DGCMD_SET_LMP             0x01
 #define DWC3_DEPCMD_TYPE_BULK          2
 #define DWC3_DEPCMD_TYPE_INTR          3
 
+/* OTG Control Register */
+#define DWC3_OTG_OCTL_PERIMODE         (1 << 6)
+
+/* OTG Events Register */
+#define DWC3_OEVT_DEVICEMODE                   (1 << 31)
+#define DWC3_OEVT_CLEAR_ALL                    (~DWC3_OEVT_DEVICEMODE)
+#define DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT                (1 << 24)
+#define DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT                (1 << 8)
+
+/* OTG Status Register */
+#define DWC3_OTG_OSTS_BSESVALID                (1 << 2)
+#define DWC3_OTG_OSTS_CONIDSTS         (1 << 0)
+
 #define DWC3_DEV_IMOD_COUNT_SHIFT      16
 #define DWC3_DEV_IMOD_COUNT_MASK       (0xffff << 16)
 #define DWC3_DEV_IMOD_INTERVAL_SHIFT   0
@@ -512,6 +568,7 @@ struct dwc3_event_buffer {
 #define DWC3_EP_DIRECTION_RX   false
 
 #define DWC3_TRB_NUM           256
+#define DWC3_TRB_MASK          (DWC3_TRB_NUM - 1)
 
 /**
  * struct dwc3_ep - device side endpoint representation
@@ -550,6 +607,8 @@ struct dwc3_ep {
 
        struct dwc3_trb         *trb_pool;
        dma_addr_t              trb_pool_dma;
+       u32                     free_slot;
+       u32                     busy_slot;
        struct dwc3             *dwc;
 
        u32                     saved_state;
@@ -781,6 +840,7 @@ struct dwc3_scratchpad_array {
  * @xhci_resources: struct resources for our @xhci child
  * @ev_buf: struct dwc3_event_buffer pointer
  * @eps: endpoint array
+ * @dotg: pointer to OTG
  * @gadget: device side representation of the peripheral controller
  * @gadget_driver: pointer to the gadget driver
  * @regs: base address for our registers
@@ -822,6 +882,10 @@ struct dwc3_scratchpad_array {
  * @lpm_nyet_threshold: LPM NYET response threshold
  * @hird_threshold: HIRD threshold
  * @hsphy_interface: "utmi" or "ulpi"
+ * @vbus_session: Indicates if the gadget was powered by the otg driver
+ * @softconnect: Indicates if pullup was issued by the usb_gadget_driver
+ * @disconnect: signals that Disconnection interrupt happend
+ * @suspend_clk_freq: frequency of suspend clock
  * @connected: true when we're connected to a host, false otherwise
  * @delayed_status: true when gadget driver asks for delayed status
  * @ep0_bounced: true when we used bounce buffer
@@ -844,6 +908,7 @@ struct dwc3_scratchpad_array {
  * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
  * @req_p1p2p3_quirk: set if we enable request p1p2p3 quirk
  * @del_p1p2p3_quirk: set if we enable delay p1p2p3 quirk
+ * @u1u2_exitfail_to_recov_quirk: set if we enable u1u2 exitfail to recov quirk
  * @del_phy_power_chg_quirk: set if we enable delay phy power change quirk
  * @lfps_filter_quirk: set if we enable LFPS filter quirk
  * @rx_detect_poll_quirk: set if we enable rx_detect to polling lfps quirk
@@ -867,6 +932,7 @@ struct dwc3_scratchpad_array {
  *     3       - Reserved
  * @imod_interval: set the interrupt moderation interval in 250ns
  *                 increments or 0 to disable.
+ * @adj_sof_accuracy: set to adjust sof accuracy
  */
 struct dwc3 {
        struct work_struct      drd_work;
@@ -891,6 +957,7 @@ struct dwc3 {
 
        struct dwc3_event_buffer *ev_buf;
        struct dwc3_ep          *eps[DWC3_ENDPOINTS_NUM];
+       struct dwc3_otg         *dotg;
 
        struct usb_gadget       gadget;
        struct usb_gadget_driver *gadget_driver;
@@ -984,6 +1051,19 @@ struct dwc3 {
 
        const char              *hsphy_interface;
 
+       bool                    vbus_session;
+       bool                    softconnect;
+
+       struct completion       disconnect;
+
+       /**
+        * Frequency of suspend clock.
+        * Suspend clock is a clock source of 16KHz clock for a small part
+        * of the USB3 core that operates when the SS PHY is in its lowest
+        * power (P3) state.
+        */
+       u32                     suspend_clk_freq;
+
        unsigned                connected:1;
        unsigned                delayed_status:1;
        unsigned                ep0_bounced:1;
@@ -1003,7 +1083,8 @@ struct dwc3 {
        unsigned                u2exit_lfps_quirk:1;
        unsigned                u2ss_inp3_quirk:1;
        unsigned                req_p1p2p3_quirk:1;
-       unsigned                del_p1p2p3_quirk:1;
+       unsigned        del_p1p2p3_quirk:1;
+       unsigned                u1u2_exitfail_to_recov_quirk:1;
        unsigned                del_phy_power_chg_quirk:1;
        unsigned                lfps_filter_quirk:1;
        unsigned                rx_detect_poll_quirk:1;
@@ -1019,6 +1100,12 @@ struct dwc3 {
        unsigned                tx_de_emphasis:2;
 
        u16                     imod_interval;
+
+       unsigned                adj_sof_accuracy:1;
+
+       unsigned                sparse_transfer_control:1;
+       unsigned                is_not_vbus_pad:1;
+       unsigned                start_config_issued:1;
 };
 
 #define work_to_dwc(w)         (container_of((w), struct dwc3, drd_work))
@@ -1176,6 +1263,17 @@ struct dwc3_gadget_ep_cmd_params {
 
 /* prototypes */
 void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
+int dwc3_soft_reset(struct dwc3 *dwc);
+int dwc3_event_buffers_setup(struct dwc3 *dwc);
+void dwc3_event_buffers_cleanup(struct dwc3 *dwc);
+int dwc3_phy_setup(struct dwc3 *dwc);
+int dwc3_core_init(struct dwc3 *dwc);
+void dwc3_core_exit(struct dwc3 *dwc);
+int dwc3_otg_start(struct dwc3 *dwc);
+void dwc3_otg_stop(struct dwc3 *dwc);
+int dwc3_otg_init(struct dwc3 *dwc);
+void dwc3_otg_exit(struct dwc3 *dwc);
+
 u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
 
 /* check whether we are on the DWC_usb3 core */
@@ -1211,6 +1309,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
 int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
                struct dwc3_gadget_ep_cmd_params *params);
 int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param);
+void dwc3_gadget_disconnect_proc(struct dwc3 *dwc);
 #else
 static inline int dwc3_gadget_init(struct dwc3 *dwc)
 { return 0; }
@@ -1230,6 +1329,8 @@ static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
 static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
                int cmd, u32 param)
 { return 0; }
+static inline void dwc3_gadget_disconnect_proc(struct dwc3 *dwc)
+{ }
 #endif
 
 #if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
index 2765c51c7ef5fe0c0242ef0f09750fae2d727598..f5c8d1f7f597447d3dc77ac136e87de165723864 100644 (file)
@@ -32,9 +32,14 @@ static void dwc3_drd_update(struct dwc3 *dwc)
        if (id < 0)
                id = 0;
 
-       dwc3_set_mode(dwc, id ?
-                     DWC3_GCTL_PRTCAP_HOST :
-                     DWC3_GCTL_PRTCAP_DEVICE);
+       /* Force set OTG mode, modify this after device bring-up */
+       dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
+
+       /* Force set OTG mode, modify this after device bring-up
+       *dwc3_set_mode(dwc, id ?
+       *             DWC3_GCTL_PRTCAP_HOST :
+       *             DWC3_GCTL_PRTCAP_DEVICE);
+       */
 }
 
 static int dwc3_drd_notifier(struct notifier_block *nb,
@@ -79,7 +84,8 @@ void dwc3_drd_exit(struct dwc3 *dwc)
        extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
                                   &dwc->edev_nb);
 
-       dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+       /*dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);*/
+       dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
        flush_work(&dwc->drd_work);
        dwc3_gadget_exit(dwc);
 }
index e089df72f766eece45c73ef652c7dfe2aab6da5e..353e1b588b355ed4248f55c9d4a5814f2c6283f4 100644 (file)
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/platform_device.h>
+#include <linux/mutex.h>
 #include <linux/clk.h>
 #include <linux/usb/otg.h>
 #include <linux/usb/usb_phy_generic.h>
+#include <linux/usb/samsung_usb.h>
+#include <linux/dma-mapping.h>
 #include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
+
+#include <linux/io.h>
+#include <linux/usb/otg-fsm.h>
+
+#ifdef CONFIG_CPU_IDLE
+#include <soc/samsung/exynos-cpupm.h>
+#endif
+
+/* -------------------------------------------------------------------------- */
+
+struct dwc3_exynos_rsw {
+       struct otg_fsm          *fsm;
+       struct work_struct      work;
+};
 
 struct dwc3_exynos {
        struct platform_device  *usb2_phy;
        struct platform_device  *usb3_phy;
        struct device           *dev;
 
-       struct clk              *clk;
-       struct clk              *susp_clk;
-       struct clk              *axius_clk;
+       struct clk              **clocks;
 
        struct regulator        *vdd33;
        struct regulator        *vdd10;
+
+       int                     idle_ip_index;
+
+       struct dwc3_exynos_rsw  rsw;
+};
+
+void dwc3_otg_run_sm(struct otg_fsm *fsm);
+
+static const struct of_device_id exynos_dwc3_match[] = {
+       {
+               .compatible = "samsung,exynos-dwusb",
+       },
+       {},
 };
+MODULE_DEVICE_TABLE(of, exynos_dwc3_match);
+
+/* -------------------------------------------------------------------------- */
+
+static int dwc3_exynos_clk_get(struct dwc3_exynos *exynos)
+{
+       struct device *dev = exynos->dev;
+       const char **clk_ids;
+       struct clk *clk;
+       int clk_count;
+       int ret, i;
+
+       clk_count = of_property_count_strings(dev->of_node, "clock-names");
+       if (IS_ERR_VALUE((unsigned long)clk_count)) {
+               dev_err(dev, "invalid clk list in %s node", dev->of_node->name);
+               return -EINVAL;
+       }
+
+       clk_ids = (const char **)devm_kmalloc(dev,
+                               (clk_count + 1) * sizeof(const char *),
+                               GFP_KERNEL);
+       if (!clk_ids) {
+               dev_err(dev, "failed to alloc for clock ids");
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < clk_count; i++) {
+               ret = of_property_read_string_index(dev->of_node, "clock-names",
+                                                               i, &clk_ids[i]);
+               if (ret) {
+                       dev_err(dev, "failed to read clocks name %d from %s node\n",
+                                       i, dev->of_node->name);
+                       return ret;
+               }
+       }
+       clk_ids[clk_count] = NULL;
+
+       exynos->clocks = (struct clk **) devm_kmalloc(exynos->dev,
+                       clk_count * sizeof(struct clk *), GFP_KERNEL);
+       if (!exynos->clocks) {
+               dev_err(exynos->dev, "%s: couldn't alloc\n", __func__);
+               return -ENOMEM;
+       }
+
+       for (i = 0; clk_ids[i] != NULL; i++) {
+               clk = devm_clk_get(exynos->dev, clk_ids[i]);
+               if (IS_ERR_OR_NULL(clk))
+                       goto err;
+
+               exynos->clocks[i] = clk;
+       }
+       exynos->clocks[i] = NULL;
+
+       return 0;
+
+err:
+       dev_err(exynos->dev, "couldn't get %s clock\n", clk_ids[i]);
+       return -EINVAL;
+}
+
+static int dwc3_exynos_clk_prepare(struct dwc3_exynos *exynos)
+{
+       int i;
+       int ret;
+
+       for (i = 0; exynos->clocks[i] != NULL; i++) {
+               ret = clk_prepare(exynos->clocks[i]);
+               if (ret)
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       dev_err(exynos->dev, "couldn't prepare clock[%d]\n", i);
+
+       /* roll back */
+       for (i = i - 1; i >= 0; i--)
+               clk_unprepare(exynos->clocks[i]);
+
+       return ret;
+}
+
+static int dwc3_exynos_clk_enable(struct dwc3_exynos *exynos)
+{
+       int i;
+       int ret;
+
+       for (i = 0; exynos->clocks[i] != NULL; i++) {
+               ret = clk_enable(exynos->clocks[i]);
+               if (ret)
+                       goto err;
+       }
+
+       return 0;
+
+err:
+       dev_err(exynos->dev, "couldn't enable clock[%d]\n", i);
+
+       /* roll back */
+       for (i = i - 1; i >= 0; i--)
+               clk_disable(exynos->clocks[i]);
+
+       return ret;
+}
+
+static void dwc3_exynos_clk_unprepare(struct dwc3_exynos *exynos)
+{
+       int i;
+
+       for (i = 0; exynos->clocks[i] != NULL; i++)
+               clk_unprepare(exynos->clocks[i]);
+}
+
+static void dwc3_exynos_clk_disable(struct dwc3_exynos *exynos)
+{
+       int i;
+
+       for (i = 0; exynos->clocks[i] != NULL; i++)
+               clk_disable(exynos->clocks[i]);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static struct dwc3_exynos *dwc3_exynos_match(struct device *dev)
+{
+       const struct of_device_id *matches = NULL;
+       struct dwc3_exynos *exynos = NULL;
+
+       if (!dev)
+               return NULL;
+
+       matches = exynos_dwc3_match;
+
+       if (of_match_device(matches, dev))
+               exynos = dev_get_drvdata(dev);
+
+       return exynos;
+}
+
+bool dwc3_exynos_rsw_available(struct device *dev)
+{
+       struct dwc3_exynos *exynos;
+
+       exynos = dwc3_exynos_match(dev);
+       if (!exynos)
+               return false;
+
+       return true;
+}
+
+int dwc3_exynos_rsw_start(struct device *dev)
+{
+       struct dwc3_exynos      *exynos = dev_get_drvdata(dev);
+       struct dwc3_exynos_rsw  *rsw = &exynos->rsw;
+
+       dev_info(dev, "%s\n", __func__);
+
+       /* B-device by default */
+       rsw->fsm->id = 1;
+       rsw->fsm->b_sess_vld = 0;
+
+       return 0;
+}
+
+void dwc3_exynos_rsw_stop(struct device *dev)
+{
+       dev_info(dev, "%s\n", __func__);
+}
+
+static void dwc3_exynos_rsw_work(struct work_struct *w)
+{
+       struct dwc3_exynos_rsw  *rsw = container_of(w,
+                                       struct dwc3_exynos_rsw, work);
+       struct dwc3_exynos      *exynos = container_of(rsw,
+                                       struct dwc3_exynos, rsw);
+
+       dev_info(exynos->dev, "%s\n", __func__);
+
+       dwc3_otg_run_sm(rsw->fsm);
+}
+
+int dwc3_exynos_rsw_setup(struct device *dev, struct otg_fsm *fsm)
+{
+       struct dwc3_exynos      *exynos = dev_get_drvdata(dev);
+       struct dwc3_exynos_rsw  *rsw = &exynos->rsw;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       INIT_WORK(&rsw->work, dwc3_exynos_rsw_work);
+
+       rsw->fsm = fsm;
+
+       return 0;
+}
+
+void dwc3_exynos_rsw_exit(struct device *dev)
+{
+       struct dwc3_exynos      *exynos = dev_get_drvdata(dev);
+       struct dwc3_exynos_rsw  *rsw = &exynos->rsw;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       cancel_work_sync(&rsw->work);
+
+       rsw->fsm = NULL;
+}
+
+/**
+ * dwc3_exynos_id_event - receive ID pin state change event.
+ * @state : New ID pin state.
+ */
+int dwc3_exynos_id_event(struct device *dev, int state)
+{
+       struct dwc3_exynos      *exynos;
+       struct dwc3_exynos_rsw  *rsw;
+       struct otg_fsm          *fsm;
+
+       dev_dbg(dev, "EVENT: ID: %d\n", state);
+
+       exynos = dev_get_drvdata(dev);
+       if (!exynos)
+               return -ENOENT;
+
+       rsw = &exynos->rsw;
+
+       fsm = rsw->fsm;
+       if (!fsm)
+               return -ENOENT;
+
+       if (fsm->id != state) {
+               fsm->id = state;
+               schedule_work(&rsw->work);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(dwc3_exynos_id_event);
+
+/**
+ * dwc3_exynos_vbus_event - receive VBus change event.
+ * vbus_active : New VBus state, true if active, false otherwise.
+ */
+int dwc3_exynos_vbus_event(struct device *dev, bool vbus_active)
+{
+       struct dwc3_exynos      *exynos;
+       struct dwc3_exynos_rsw  *rsw;
+       struct otg_fsm          *fsm;
+
+       dev_dbg(dev, "EVENT: VBUS: %sactive\n", vbus_active ? "" : "in");
+
+       exynos = dev_get_drvdata(dev);
+       if (!exynos)
+               return -ENOENT;
+
+       rsw = &exynos->rsw;
+
+       fsm = rsw->fsm;
+       if (!fsm)
+               return -ENOENT;
+
+       if (fsm->b_sess_vld != vbus_active) {
+               fsm->b_sess_vld = vbus_active;
+               schedule_work(&rsw->work);
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(dwc3_exynos_vbus_event);
 
 static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos)
 {
@@ -112,65 +410,68 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
 
        int                     ret;
 
+       pr_info("%s: +++\n", __func__);
        exynos = devm_kzalloc(dev, sizeof(*exynos), GFP_KERNEL);
        if (!exynos)
                return -ENOMEM;
 
+       ret = dma_set_mask(dev, DMA_BIT_MASK(36));
+       if (ret) {
+               pr_err("dma set mask ret = %d\n", ret);
+               return ret;
+       }
+
        platform_set_drvdata(pdev, exynos);
 
-       exynos->dev     = dev;
+       exynos->dev = dev;
 
-       exynos->clk = devm_clk_get(dev, "usbdrd30");
-       if (IS_ERR(exynos->clk)) {
-               dev_err(dev, "couldn't get clock\n");
-               return -EINVAL;
-       }
-       ret = clk_prepare_enable(exynos->clk);
+#ifdef CONFIG_CPU_IDLE
+       exynos->idle_ip_index = exynos_get_idle_ip_index(dev_name(dev));
+       exynos_update_ip_idle_status(exynos->idle_ip_index, 0);
+#endif
+
+       ret = dwc3_exynos_clk_get(exynos);
        if (ret)
                return ret;
 
-       exynos->susp_clk = devm_clk_get(dev, "usbdrd30_susp_clk");
-       if (IS_ERR(exynos->susp_clk))
-               exynos->susp_clk = NULL;
-       ret = clk_prepare_enable(exynos->susp_clk);
+       ret = dwc3_exynos_clk_prepare(exynos);
        if (ret)
-               goto susp_clk_err;
-
-       if (of_device_is_compatible(node, "samsung,exynos7-dwusb3")) {
-               exynos->axius_clk = devm_clk_get(dev, "usbdrd30_axius_clk");
-               if (IS_ERR(exynos->axius_clk)) {
-                       dev_err(dev, "no AXI UpScaler clk specified\n");
-                       ret = -ENODEV;
-                       goto axius_clk_err;
-               }
-               ret = clk_prepare_enable(exynos->axius_clk);
-               if (ret)
-                       goto axius_clk_err;
-       } else {
-               exynos->axius_clk = NULL;
+               return ret;
+
+       ret = dwc3_exynos_clk_enable(exynos);
+       if (ret) {
+               dwc3_exynos_clk_unprepare(exynos);
+               return ret;
        }
 
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+
        exynos->vdd33 = devm_regulator_get(dev, "vdd33");
        if (IS_ERR(exynos->vdd33)) {
-               ret = PTR_ERR(exynos->vdd33);
-               goto vdd33_err;
+               dev_dbg(dev, "couldn't get regulator vdd33\n");
+               exynos->vdd33 = NULL;
        }
-       ret = regulator_enable(exynos->vdd33);
-       if (ret) {
-               dev_err(dev, "Failed to enable VDD33 supply\n");
-               goto vdd33_err;
+       if (exynos->vdd33) {
+               ret = regulator_enable(exynos->vdd33);
+               if (ret) {
+                       dev_err(dev, "Failed to enable VDD33 supply\n");
+                       goto vdd33_err;
+               }
        }
 
        exynos->vdd10 = devm_regulator_get(dev, "vdd10");
        if (IS_ERR(exynos->vdd10)) {
-               ret = PTR_ERR(exynos->vdd10);
-               goto vdd10_err;
-       }
-       ret = regulator_enable(exynos->vdd10);
-       if (ret) {
-               dev_err(dev, "Failed to enable VDD10 supply\n");
-               goto vdd10_err;
+               dev_dbg(dev, "couldn't get regulator vdd10\n");
+               exynos->vdd10 = NULL;
        }
+       if (exynos->vdd10) {
+               ret = regulator_enable(exynos->vdd10);
+               if (ret) {
+                       dev_err(dev, "Failed to enable VDD10 supply\n");
+                       goto vdd10_err;
+               }
+        }
 
        ret = dwc3_exynos_register_phys(exynos);
        if (ret) {
@@ -190,21 +491,23 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
                goto populate_err;
        }
 
+       pr_info("%s: ---\n", __func__);
        return 0;
 
 populate_err:
        platform_device_unregister(exynos->usb2_phy);
        platform_device_unregister(exynos->usb3_phy);
 phys_err:
-       regulator_disable(exynos->vdd10);
+       if (exynos->vdd10)
+               regulator_disable(exynos->vdd10);
 vdd10_err:
-       regulator_disable(exynos->vdd33);
+       if (exynos->vdd33)
+               regulator_disable(exynos->vdd33);
 vdd33_err:
-       clk_disable_unprepare(exynos->axius_clk);
-axius_clk_err:
-       clk_disable_unprepare(exynos->susp_clk);
-susp_clk_err:
-       clk_disable_unprepare(exynos->clk);
+       pm_runtime_disable(&pdev->dev);
+       dwc3_exynos_clk_disable(exynos);
+       dwc3_exynos_clk_unprepare(exynos);
+       pm_runtime_set_suspended(&pdev->dev);
        return ret;
 }
 
@@ -216,33 +519,80 @@ static int dwc3_exynos_remove(struct platform_device *pdev)
        platform_device_unregister(exynos->usb2_phy);
        platform_device_unregister(exynos->usb3_phy);
 
-       clk_disable_unprepare(exynos->axius_clk);
-       clk_disable_unprepare(exynos->susp_clk);
-       clk_disable_unprepare(exynos->clk);
+       pm_runtime_disable(&pdev->dev);
+       if (!pm_runtime_status_suspended(&pdev->dev)) {
+               dwc3_exynos_clk_disable(exynos);
+               pm_runtime_set_suspended(&pdev->dev);
+       }
+
+       dwc3_exynos_clk_unprepare(exynos);
 
-       regulator_disable(exynos->vdd33);
-       regulator_disable(exynos->vdd10);
+       if (exynos->vdd33)
+               regulator_disable(exynos->vdd33);
+       if (exynos->vdd10)
+               regulator_disable(exynos->vdd10);
 
        return 0;
 }
 
-static const struct of_device_id exynos_dwc3_match[] = {
-       { .compatible = "samsung,exynos5250-dwusb3" },
-       { .compatible = "samsung,exynos7-dwusb3" },
-       {},
-};
-MODULE_DEVICE_TABLE(of, exynos_dwc3_match);
+#ifdef CONFIG_PM
+static int dwc3_exynos_runtime_suspend(struct device *dev)
+{
+       struct dwc3_exynos *exynos = dev_get_drvdata(dev);
+
+       dev_info(dev, "%s\n", __func__);
+
+       dwc3_exynos_clk_disable(exynos);
+
+#ifdef CONFIG_CPU_IDLE
+       /* inform what USB state is idle to IDLE_IP */
+       exynos_update_ip_idle_status(exynos->idle_ip_index, 1);
+#endif
+
+       return 0;
+}
+
+static int dwc3_exynos_runtime_resume(struct device *dev)
+{
+       struct dwc3_exynos *exynos = dev_get_drvdata(dev);
+       int ret = 0;
+
+       dev_info(dev, "%s\n", __func__);
+
+#ifdef CONFIG_CPU_IDLE
+       /* inform what USB state is not idle to IDLE_IP */
+       exynos_update_ip_idle_status(exynos->idle_ip_index, 0);
+#endif
+
+       ret = dwc3_exynos_clk_enable(exynos);
+       if (ret) {
+               dev_err(dev, "%s: clk_enable failed\n", __func__);
+               return ret;
+       }
+
+       return 0;
+}
+#endif
 
 #ifdef CONFIG_PM_SLEEP
 static int dwc3_exynos_suspend(struct device *dev)
 {
        struct dwc3_exynos *exynos = dev_get_drvdata(dev);
 
-       clk_disable(exynos->axius_clk);
-       clk_disable(exynos->clk);
+       dev_dbg(dev, "%s\n", __func__);
+
+       if (pm_runtime_suspended(dev))
+               return 0;
+
+       dwc3_exynos_clk_disable(exynos);
 
-       regulator_disable(exynos->vdd33);
-       regulator_disable(exynos->vdd10);
+       if (exynos->vdd33)
+               regulator_disable(exynos->vdd33);
+       if (exynos->vdd10)
+               regulator_disable(exynos->vdd10);
+
+       /* inform what USB state is idle to IDLE_IP */
+       //exynos_update_ip_idle_status(exynos->idle_ip_index, 1);
 
        return 0;
 }
@@ -252,20 +602,34 @@ static int dwc3_exynos_resume(struct device *dev)
        struct dwc3_exynos *exynos = dev_get_drvdata(dev);
        int ret;
 
-       ret = regulator_enable(exynos->vdd33);
-       if (ret) {
-               dev_err(dev, "Failed to enable VDD33 supply\n");
-               return ret;
+       dev_dbg(dev, "%s\n", __func__);
+
+       /* inform what USB state is not idle to IDLE_IP */
+       //exynos_update_ip_idle_status(exynos->idle_ip_index, 0);
+
+       if (exynos->vdd33) {
+               ret = regulator_enable(exynos->vdd33);
+               if (ret) {
+                       dev_err(dev, "Failed to enable VDD33 supply\n");
+                       return ret;
+               }
+       }
+       if (exynos->vdd10) {
+               ret = regulator_enable(exynos->vdd10);
+               if (ret) {
+                       if (exynos->vdd33)
+                               regulator_disable(exynos->vdd33);
+                       dev_err(dev, "Failed to enable VDD10 supply\n");
+                       return ret;
+               }
        }
-       ret = regulator_enable(exynos->vdd10);
+
+       ret = dwc3_exynos_clk_enable(exynos);
        if (ret) {
-               dev_err(dev, "Failed to enable VDD10 supply\n");
+               dev_err(dev, "%s: clk_enable failed\n", __func__);
                return ret;
        }
 
-       clk_enable(exynos->clk);
-       clk_enable(exynos->axius_clk);
-
        /* runtime set active to reflect active state. */
        pm_runtime_disable(dev);
        pm_runtime_set_active(dev);
@@ -276,6 +640,8 @@ static int dwc3_exynos_resume(struct device *dev)
 
 static const struct dev_pm_ops dwc3_exynos_dev_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(dwc3_exynos_suspend, dwc3_exynos_resume)
+       SET_RUNTIME_PM_OPS(dwc3_exynos_runtime_suspend,
+                       dwc3_exynos_runtime_resume, NULL)
 };
 
 #define DEV_PM_OPS     (&dwc3_exynos_dev_pm_ops)
index 89fe53c846ef98424507cc9846bd49cc92a41ad0..f1caa03926f6e48a382822ca34f5f27b7da1eed1 100644 (file)
@@ -232,6 +232,15 @@ static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
 {
        struct dwc3_ep          *dep;
 
+       if (dwc->eps[1]->endpoint.desc == NULL) {
+               dev_err(dwc->dev, "EP1 was disabled: DESC NULL\n");
+               return;
+       }
+       if (dwc->eps[0]->endpoint.desc == NULL) {
+               dev_err(dwc->dev, "EP0 was disabled: DESC NULL\n");
+               return;
+       }
+
        /* reinitialize physical ep1 */
        dep = dwc->eps[1];
        dep->flags = DWC3_EP_ENABLED;
@@ -390,6 +399,10 @@ static int dwc3_ep0_handle_u1(struct dwc3 *dwc, enum usb_device_state state,
                        (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
                return -EINVAL;
 
+       /* see NEGATIVE RX DETECTION comment */
+       if (set && dwc->revision < DWC3_REVISION_230A)
+               return 0;
+
        reg = dwc3_readl(dwc->regs, DWC3_DCTL);
        if (set)
                reg |= DWC3_DCTL_INITU1ENA;
@@ -412,6 +425,10 @@ static int dwc3_ep0_handle_u2(struct dwc3 *dwc, enum usb_device_state state,
                        (dwc->speed != DWC3_DSTS_SUPERSPEED_PLUS))
                return -EINVAL;
 
+       /* see NEGATIVE RX DETECTION comment */
+       if (set && dwc->revision < DWC3_REVISION_230A)
+               return 0;
+
        reg = dwc3_readl(dwc->regs, DWC3_DCTL);
        if (set)
                reg |= DWC3_DCTL_INITU2ENA;
@@ -642,12 +659,25 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
                                                USB_STATE_CONFIGURED);
 
                        /*
-                        * Enable transition to U1/U2 state when
-                        * nothing is pending from application.
+                        * NEGATIVE RX DETECTION
+                        * Some host controllers (e.g. Intel) perform far-end
+                        * receiver termination _negative_ detection while link
+                        * is in U2 state. Synopsys PIPE PHY considers this
+                        * signalling as U2 LFPS exit, moves to Recovery state
+                        * and waits for training sequence which never comes.
+                        * This finally leads to reconnection. Starting from
+                        * DWC3 core 2.30a, GCTL register has bit U2EXIT_LFPS,
+                        * which improves interoperability with such HCs.
                         */
-                       reg = dwc3_readl(dwc->regs, DWC3_DCTL);
-                       reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA);
-                       dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+                       if (dwc->revision >= DWC3_REVISION_230A) {
+                               /*
+                                * Enable transition to U1/U2 state when
+                                * nothing is pending from application.
+                                */
+                               reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+                               reg |= (DWC3_DCTL_ACCEPTU1ENA | DWC3_DCTL_ACCEPTU2ENA);
+                               dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+                       }
                }
                break;
 
index 0ebdb313bb00b0efa11de13aa1da93cd8a9e7f64..a9ef1ad3f05b5b9385c68b85cd7540df8f76b83b 100644 (file)
 
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
+#include <linux/usb/samsung_usb.h>
+#include <linux/phy/phy.h>
 
 #include "debug.h"
 #include "core.h"
+#include "otg.h"
 #include "gadget.h"
 #include "io.h"
 
@@ -188,19 +191,33 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
                int status)
 {
        struct dwc3                     *dwc = dep->dwc;
+       unsigned int                    unmap_after_complete = false;
 
        req->started = false;
-       list_del(&req->list);
+       /* Only delete from the list if the item isn't poisoned. */
+       if (req->list.next != LIST_POISON1)
+               list_del(&req->list);
        req->remaining = 0;
 
        if (req->request.status == -EINPROGRESS)
                req->request.status = status;
 
-       if (req->trb)
-               usb_gadget_unmap_request_by_dev(dwc->sysdev,
+       /*
+        * NOTICE we don't want to unmap before calling ->complete() if we're
+        * dealing with a bounced ep0 request. If we unmap it here, we would end
+        * up overwritting the contents of req->buf and this could confuse the
+        * gadget driver.
+        */
+       if (req->trb) {
+               if (dwc->ep0_bounced && dep->number <= 1) {
+                       dwc->ep0_bounced = false;
+                       unmap_after_complete = true;
+               } else {
+                       usb_gadget_unmap_request_by_dev(dwc->sysdev,
                                                &req->request, req->direction);
-
-       req->trb = NULL;
+                       req->trb = NULL;
+               }
+       }
 
        trace_dwc3_gadget_giveback(req);
 
@@ -208,6 +225,11 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
        usb_gadget_giveback_request(&dep->endpoint, &req->request);
        spin_lock(&dwc->lock);
 
+       if (unmap_after_complete)
+               usb_gadget_unmap_request_by_dev(dwc->sysdev,
+                               &req->request, req->direction);
+       req->trb = NULL;
+
        if (dep->number > 1)
                pm_runtime_put(dwc->dev);
 }
@@ -300,8 +322,11 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
 
                if (unlikely(needs_wakeup)) {
                        ret = __dwc3_gadget_wakeup(dwc);
-                       dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n",
-                                       ret);
+                       /*
+                        * disable warning when muic/ccic wasn't merged
+                        *dev_WARN_ONCE(dwc->dev, ret, "wakeup failed --> %d\n",
+                        *              ret);
+                        */
                }
        }
 
@@ -1313,6 +1338,12 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
 
        list_add_tail(&req->list, &dep->pending_list);
 
+       /* prevent starting transfer if controller is stopped */
+       if (!dwc->pullups_connected) {
+               dev_dbg(dwc->dev, "queue request while udc is stopped");
+               return 0;
+       }
+
        /*
         * NOTICE: Isochronous endpoints should NEVER be prestarted. We must
         * wait for a XferNotReady event so we will know what's the current
@@ -1491,6 +1522,9 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
        struct dwc3                             *dwc = dep->dwc;
        int                                     ret;
 
+       if (dep->endpoint.desc == NULL)
+               return -EINVAL;
+
        if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
                dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name);
                return -EINVAL;
@@ -1504,9 +1538,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
                unsigned transfer_in_flight;
                unsigned started;
 
-               if (dep->flags & DWC3_EP_STALL)
-                       return 0;
-
                if (dep->number > 1)
                        trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue);
                else
@@ -1528,9 +1559,6 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
                else
                        dep->flags |= DWC3_EP_STALL;
        } else {
-               if (!(dep->flags & DWC3_EP_STALL))
-                       return 0;
-
                ret = dwc3_send_clear_stall_ep_cmd(dep);
                if (ret)
                        dev_err(dwc->dev, "failed to clear STALL on %s\n",
@@ -1708,16 +1736,125 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
        return 0;
 }
 
+static int dwc3_udc_init(struct dwc3 *dwc)
+{
+       struct dwc3_ep          *dep;
+       u32                     reg;
+       int                     ret = 0;
+
+       reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+       reg &= ~(DWC3_DCFG_SPEED_MASK);
+
+       /**
+        * WORKAROUND: DWC3 revision < 2.20a have an issue
+        * which would cause metastability state on Run/Stop
+        * bit if we try to force the IP to USB2-only mode.
+        *
+        * Because of that, we cannot configure the IP to any
+        * speed other than the SuperSpeed
+        *
+        * Refers to:
+        *
+        * STAR#9000525659: Clock Domain Crossing on DCTL in
+        * USB 2.0 Mode
+        */
+       if (dwc->revision < DWC3_REVISION_220A) {
+               reg |= DWC3_DCFG_SUPERSPEED;
+       } else {
+               switch (dwc->maximum_speed) {
+               case USB_SPEED_LOW:
+                       reg |= DWC3_DSTS_LOWSPEED;
+                       break;
+               case USB_SPEED_FULL:
+                       reg |= DWC3_DSTS_FULLSPEED1;
+                       break;
+               case USB_SPEED_HIGH:
+                       reg |= DWC3_DSTS_HIGHSPEED;
+                       break;
+               case USB_SPEED_SUPER:   /* FALLTHROUGH */
+               case USB_SPEED_UNKNOWN: /* FALTHROUGH */
+               default:
+                       reg |= DWC3_DSTS_SUPERSPEED;
+               }
+       }
+       dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+       /* Start with SuperSpeed Default */
+       dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
+
+       dep = dwc->eps[0];
+       ret = __dwc3_gadget_ep_enable(dep, false, false);
+       if (ret) {
+               dev_err(dwc->dev, "failed to enable %s\n", dep->name);
+               goto err0;
+       }
+
+       dep = dwc->eps[1];
+       ret = __dwc3_gadget_ep_enable(dep, false, false);
+       if (ret) {
+               dev_err(dwc->dev, "failed to enable %s\n", dep->name);
+               goto err1;
+       }
+
+       /* begin to receive SETUP packets */
+       dwc->ep0state = EP0_SETUP_PHASE;
+       dwc3_ep0_out_start(dwc);
+
+       return 0;
+
+err1:
+       __dwc3_gadget_ep_disable(dwc->eps[0]);
+
+err0:
+       return ret;
+}
+
+static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
+{
+       u32                     reg;
+
+       /* Enable all but Start and End of Frame IRQs */
+       reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
+                       DWC3_DEVTEN_EVNTOVERFLOWEN |
+                       DWC3_DEVTEN_CMDCMPLTEN |
+                       DWC3_DEVTEN_ERRTICERREN |
+                       DWC3_DEVTEN_WKUPEVTEN |
+                       DWC3_DEVTEN_U3L2_SUSPEN |
+                       DWC3_DEVTEN_ULSTCNGEN |
+                       DWC3_DEVTEN_CONNECTDONEEN |
+                       DWC3_DEVTEN_USBRSTEN |
+                       DWC3_DEVTEN_DISCONNEVTEN);
+
+       dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
+}
+
+static void dwc3_gadget_disable_irq(struct dwc3 *dwc)
+{
+       /* mask all interrupts */
+       dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
+}
+
 static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
 {
        u32                     reg;
-       u32                     timeout = 500;
+       u32                     timeout = 1000;
+       int                     retries = 1000;
+       int                     ret = 0;
 
        if (pm_runtime_suspended(dwc->dev))
                return 0;
 
        reg = dwc3_readl(dwc->regs, DWC3_DCTL);
        if (is_on) {
+               dwc3_event_buffers_setup(dwc);
+               ret = dwc3_udc_init(dwc);
+               if (ret) {
+                       dev_err(dwc->dev, "failed to reinitialize udc\n");
+                       return ret;
+               }
+
+               dwc3_gadget_enable_irq(dwc);
+
                if (dwc->revision <= DWC3_REVISION_187A) {
                        reg &= ~DWC3_DCTL_TRGTULST_MASK;
                        reg |= DWC3_DCTL_TRGTULST_RX_DET;
@@ -1725,6 +1862,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
 
                if (dwc->revision >= DWC3_REVISION_194A)
                        reg &= ~DWC3_DCTL_KEEP_CONNECT;
+
                reg |= DWC3_DCTL_RUN_STOP;
 
                if (dwc->has_hibernation)
@@ -1732,6 +1870,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
 
                dwc->pullups_connected = true;
        } else {
+               reg = dwc3_readl(dwc->regs, DWC3_DCTL);
                reg &= ~DWC3_DCTL_RUN_STOP;
 
                if (dwc->has_hibernation && !suspend)
@@ -1744,73 +1883,250 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
 
        do {
                reg = dwc3_readl(dwc->regs, DWC3_DSTS);
-               reg &= DWC3_DSTS_DEVCTRLHLT;
-       } while (--timeout && !(!is_on ^ !reg));
+               if (is_on) {
+                       if (!(reg & DWC3_DSTS_DEVCTRLHLT))
+                               break;
+               } else {
+                       if (reg & DWC3_DSTS_DEVCTRLHLT)
+                               break;
+               }
+               timeout--;
+               if (!timeout) {
+                       if (is_on) {
+                               reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+                               reg |= DWC3_DCTL_CSFTRST;
+                               dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+                               dev_err(dwc->dev,
+                                       "gadget run/stop timeout, DCTL : 0x%x\n",
+                                       reg);
+                               reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+                               dev_err(dwc->dev,
+                                       "gadget run/stop timeout, DSTS : 0x%x\n",
+                                       reg);
+                               do {
+                                       reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+                                       if (!(reg & DWC3_DCTL_CSFTRST)) {
+                                               dev_info(dwc->dev,
+                                                       "gadget run/stop DCTL softreset, DCTL : 0x%x\n",
+                                                       reg);
+                                               goto good;
+                                       }
+                                       udelay(1);
+
+                               } while (--retries);
+
+                               return -ETIMEDOUT;
+                       }
 
-       if (!timeout)
-               return -ETIMEDOUT;
+                       /* Do nothing in DCTL stop timeout */
+                       dev_err(dwc->dev,
+                               "gadget DCTL stop timeout, DSTS: 0x%x\n",
+                               reg);
+                       goto good;
+               }
+               udelay(1);
+       } while (1);
+good:
+       return ret;
+}
 
-       return 0;
+static int dwc3_gadget_run_stop_vbus(struct dwc3 *dwc, int is_on, int suspend)
+{
+       u32                     reg;
+       u32                     timeout = 1000;
+       int                     retries = 1000;
+       int                     ret = 0;
+
+       if (pm_runtime_suspended(dwc->dev))
+               return 0;
+
+       reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+       if (is_on) {
+               dwc3_event_buffers_setup(dwc);
+               ret = dwc3_udc_init(dwc);
+               if (ret) {
+                       dev_err(dwc->dev, "failed to reinitialize udc\n");
+                       return ret;
+               }
+
+               dwc3_gadget_enable_irq(dwc);
+
+               reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+
+               if (dwc->revision <= DWC3_REVISION_187A) {
+                       reg &= ~DWC3_DCTL_TRGTULST_MASK;
+                       reg |= DWC3_DCTL_TRGTULST_RX_DET;
+               }
+
+               if (dwc->revision >= DWC3_REVISION_194A)
+                       reg &= ~DWC3_DCTL_KEEP_CONNECT;
+
+               reg |= DWC3_DCTL_RUN_STOP;
+
+               if (dwc->has_hibernation)
+                       reg |= DWC3_DCTL_KEEP_CONNECT;
+
+               dwc->pullups_connected = true;
+       } else {
+               dwc3_gadget_disable_irq(dwc);
+               dwc3_event_buffers_cleanup(dwc);
+               __dwc3_gadget_ep_disable(dwc->eps[1]);
+               __dwc3_gadget_ep_disable(dwc->eps[0]);
+
+               reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+               reg &= ~DWC3_DCTL_RUN_STOP;
+
+               if (dwc->has_hibernation && !suspend)
+                       reg &= ~DWC3_DCTL_KEEP_CONNECT;
+
+               dwc->pullups_connected = false;
+       }
+
+       dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+       do {
+               reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+               if (is_on) {
+                       if (!(reg & DWC3_DSTS_DEVCTRLHLT))
+                               break;
+               } else {
+                       if (reg & DWC3_DSTS_DEVCTRLHLT)
+                               break;
+               }
+               timeout--;
+               if (!timeout) {
+                       if (is_on) {
+                               reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+                               reg |= DWC3_DCTL_CSFTRST;
+                               dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+                               dev_err(dwc->dev,
+                                       "gadget run/stop timeout, DCTL : 0x%x\n",
+                                       reg);
+                               reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+                               dev_err(dwc->dev,
+                                       "gadget run/stop timeout, DSTS : 0x%x\n",
+                                       reg);
+                               do {
+                                       reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+                                       if (!(reg & DWC3_DCTL_CSFTRST)) {
+                                               dev_info(dwc->dev,
+                                                       "gadget run/stop DCTL softreset, DCTL : 0x%x\n",
+                                                       reg);
+                                               goto good;
+                                       }
+                                       udelay(1);
+
+                               } while (--retries);
+
+                               return -ETIMEDOUT;
+                       }
+
+                       /* Do nothing in DCTL stop timeout */
+                       dev_err(dwc->dev,
+                       "gadget DCTL stop timeout, DSTS: 0x%x\n",
+                       reg);
+                       goto good;
+               }
+               udelay(1);
+       } while (1);
+good:
+       return ret;
 }
 
-static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
+static int dwc3_gadget_vbus_session(struct usb_gadget *g, int is_active)
 {
-       struct dwc3             *dwc = gadget_to_dwc(g);
-       unsigned long           flags;
-       int                     ret;
+       struct dwc3 *dwc = gadget_to_dwc(g);
+       unsigned long flags;
+       int ret = 0;
 
-       is_on = !!is_on;
+       if (!dwc->dotg)
+               return -EPERM;
+
+       is_active = !!is_active;
+
+       spin_lock_irqsave(&dwc->lock, flags);
+
+       /* Mark that the vbus was powered */
+       dwc->vbus_session = is_active;
 
        /*
-        * Per databook, when we want to stop the gadget, if a control transfer
-        * is still in process, complete it and get the core into setup phase.
+        * Check if upper level usb_gadget_driver was already registerd with
+        * this udc controller driver (if dwc3_gadget_start was called)
         */
-       if (!is_on && dwc->ep0state != EP0_SETUP_PHASE) {
-               reinit_completion(&dwc->ep0_in_setup);
-
-               ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
-                               msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
-               if (ret == 0) {
-                       dev_err(dwc->dev, "timed out waiting for SETUP phase\n");
-                       return -ETIMEDOUT;
+       if (dwc->gadget_driver && dwc->softconnect) {
+               if (dwc->vbus_session) {
+                       /*
+                        * Both vbus was activated by otg and pullup was
+                        * signaled by the gadget driver.
+                        */
+                       ret = dwc3_gadget_run_stop_vbus(dwc, 1, false);
+               } else {
+                       ret = dwc3_gadget_run_stop_vbus(dwc, 0, false);
                }
        }
 
-       spin_lock_irqsave(&dwc->lock, flags);
-       ret = dwc3_gadget_run_stop(dwc, is_on, false);
        spin_unlock_irqrestore(&dwc->lock, flags);
 
+       if (ret)
+               dev_err(dwc->dev, "dwc3 gadget run/stop error:%d\n", ret);
+
        return ret;
 }
 
-static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
+static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
 {
-       u32                     reg;
+       struct dwc3             *dwc = gadget_to_dwc(g);
+       unsigned long           flags;
+       struct usb_otg          *otg = &(dwc->dotg->otg);
+       int                     ret;
 
-       /* Enable all but Start and End of Frame IRQs */
-       reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
-                       DWC3_DEVTEN_EVNTOVERFLOWEN |
-                       DWC3_DEVTEN_CMDCMPLTEN |
-                       DWC3_DEVTEN_ERRTICERREN |
-                       DWC3_DEVTEN_WKUPEVTEN |
-                       DWC3_DEVTEN_CONNECTDONEEN |
-                       DWC3_DEVTEN_USBRSTEN |
-                       DWC3_DEVTEN_DISCONNEVTEN);
+       is_on = !!is_on;
 
-       if (dwc->revision < DWC3_REVISION_250A)
-               reg |= DWC3_DEVTEN_ULSTCNGEN;
+       spin_lock_irqsave(&dwc->lock, flags);
 
-       dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
-}
+       dev_info(dwc->dev, "%s: pullup = %d, vbus = %d\n",
+                       __func__, is_on, dwc->vbus_session);
+       if (is_on == dwc->softconnect) {
+               dev_info(dwc->dev, "pullup is already %s\n",
+                               is_on ? "on" : "off");
+               spin_unlock_irqrestore(&dwc->lock, flags);
+               return 0;
+       }
 
-static void dwc3_gadget_disable_irq(struct dwc3 *dwc)
-{
-       /* mask all interrupts */
-       dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
+       dwc->softconnect = is_on;
+
+       if (dwc->dotg && !dwc->vbus_session) {
+               spin_unlock_irqrestore(&dwc->lock, flags);
+               /* Need to wait for vbus_session(on) from otg driver */
+               return 0;
+       }
+
+       if (is_on) {
+               dwc3_soft_reset(dwc);
+
+               phy_tune(dwc->usb2_generic_phy, otg->state);
+               phy_tune(dwc->usb3_generic_phy, otg->state);
+
+               /**
+                * In case there is not a resistance to detect VBUS,
+                * DP/DM controls by S/W are needed at this point.
+                */
+               if (dwc->is_not_vbus_pad) {
+                       phy_set(dwc->usb2_generic_phy, SET_DPPULLUP_DISABLE, NULL);
+                       phy_set(dwc->usb3_generic_phy, SET_DPPULLUP_DISABLE, NULL);
+               }
+       }
+
+       ret = dwc3_gadget_run_stop(dwc, is_on, false);
+       spin_unlock_irqrestore(&dwc->lock, flags);
+
+       return ret;
 }
 
 static irqreturn_t dwc3_interrupt(int irq, void *_dwc);
+#if IS_ENABLED(DWC3_GADGET_IRQ_ORG)
 static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc);
+#endif
 
 /**
  * dwc3_gadget_setup_nump - calculate and initialize NUMP field of %DWC3_DCFG
@@ -1859,6 +2175,8 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
        int                     ret = 0;
        u32                     reg;
 
+       dwc3_event_buffers_setup(dwc);
+
        /*
         * Use IMOD if enabled via dwc->imod_interval. Otherwise, if
         * the core supports IMOD, disable it.
@@ -1924,8 +2242,13 @@ static int dwc3_gadget_start(struct usb_gadget *g,
        int                     irq;
 
        irq = dwc->irq_gadget;
+#if IS_ENABLED(DWC3_GADGET_IRQ_ORG)
        ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
                        IRQF_SHARED, "dwc3", dwc->ev_buf);
+#else
+       ret = devm_request_irq(dwc->dev, irq, dwc3_interrupt,
+                       IRQF_SHARED, "dwc3", dwc->ev_buf);
+#endif
        if (ret) {
                dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
                                irq, ret);
@@ -1943,8 +2266,12 @@ static int dwc3_gadget_start(struct usb_gadget *g,
 
        dwc->gadget_driver      = driver;
 
-       if (pm_runtime_active(dwc->dev))
-               __dwc3_gadget_start(dwc);
+       /* Modifying Kernel 4.14 change -
+        * executing ep_enable here makes conflict
+        * with dwc3_udc_init in dwc3_gadget_run_stop
+        * if (pm_runtime_active(dwc->dev))
+        *      __dwc3_gadget_start(dwc);
+        */
 
        spin_unlock_irqrestore(&dwc->lock, flags);
 
@@ -1961,15 +2288,15 @@ err0:
 static void __dwc3_gadget_stop(struct dwc3 *dwc)
 {
        dwc3_gadget_disable_irq(dwc);
-       __dwc3_gadget_ep_disable(dwc->eps[0]);
        __dwc3_gadget_ep_disable(dwc->eps[1]);
+       __dwc3_gadget_ep_disable(dwc->eps[0]);
 }
 
 static int dwc3_gadget_stop(struct usb_gadget *g)
 {
        struct dwc3             *dwc = gadget_to_dwc(g);
        unsigned long           flags;
-       int                     epnum;
+       /*int                   epnum;*/
 
        spin_lock_irqsave(&dwc->lock, flags);
 
@@ -1978,6 +2305,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
 
        __dwc3_gadget_stop(dwc);
 
+#if defined(WAIT_EP_CMD_CMPLT_ENABLED)
        for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
                struct dwc3_ep  *dep = dwc->eps[epnum];
 
@@ -1991,6 +2319,7 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
                                    !(dep->flags & DWC3_EP_END_TRANSFER_PENDING),
                                    dwc->lock);
        }
+#endif
 
 out:
        dwc->gadget_driver      = NULL;
@@ -2062,6 +2391,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,
@@ -2397,6 +2727,9 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
        int                     clean_busy;
        u32                     is_xfer_complete;
 
+       if (dep->endpoint.desc == NULL)
+               return;
+
        is_xfer_complete = (event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE);
 
        if (event->status & DEPEVT_STATUS_BUSERR)
@@ -2607,7 +2940,8 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
        cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
        memset(&params, 0, sizeof(params));
        ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
-       WARN_ON_ONCE(ret);
+       /* WA for MTP failure, Ignore CMDACT timeout such as Kernel 4.4 */
+       /*WARN_ON_ONCE(ret);*/
        dep->resource_index = 0;
        dep->flags &= ~DWC3_EP_BUSY;
 
@@ -2656,6 +2990,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
        dwc->setup_packet_pending = false;
        usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
 
+       complete(&dwc->disconnect);
        dwc->connected = false;
 }
 
@@ -2708,8 +3043,24 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
        reg = dwc3_readl(dwc->regs, DWC3_DCFG);
        reg &= ~(DWC3_DCFG_DEVADDR_MASK);
        dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+       if (dwc->is_not_vbus_pad) {
+               phy_set(dwc->usb2_generic_phy, SET_DPPULLUP_ENABLE, NULL);
+               phy_set(dwc->usb3_generic_phy, SET_DPPULLUP_ENABLE, NULL);
+       }
 }
 
+/* Remove Warning - Check later!
+static void dwc3_update_ram_clk_sel(struct dwc3 *dwc, u32 speed)
+{
+       u32 reg;
+
+       reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+       reg |= DWC3_GCTL_RAMCLKSEL(DWC3_GCTL_CLK_MASK);
+       dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+}
+*/
+
 static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
 {
        struct dwc3_ep          *dep;
@@ -2831,10 +3182,24 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
         *
         * In both cases reset values should be sufficient.
         */
+
+       /**
+        * In case there is not a resistance to detect VBUS,
+        * DP/DM controls by S/W are needed at this point.
+        */
+       if (dwc->is_not_vbus_pad && !(speed & DWC3_DCFG_FULLSPEED1)) {
+               phy_set(dwc->usb2_generic_phy, SET_DPPULLUP_DISABLE, NULL);
+               phy_set(dwc->usb3_generic_phy, SET_DPPULLUP_DISABLE, NULL);
+       }
 }
 
 static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
 {
+       if (dwc->is_not_vbus_pad) {
+               phy_set(dwc->usb2_generic_phy, SET_DPPULLUP_DISABLE, NULL);
+               phy_set(dwc->usb3_generic_phy, SET_DPPULLUP_DISABLE, NULL);
+       }
+
        /*
         * TODO take core out of low power mode when that's
         * implemented.
@@ -2926,6 +3291,12 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
        }
 
        switch (next) {
+       case DWC3_LINK_STATE_U0:
+               if (dwc->is_not_vbus_pad) {
+                       phy_set(dwc->usb2_generic_phy, SET_DPPULLUP_ENABLE, NULL);
+                       phy_set(dwc->usb3_generic_phy, SET_DPPULLUP_ENABLE, NULL);
+               }
+               break;
        case DWC3_LINK_STATE_U1:
                if (dwc->speed == USB_SPEED_SUPER)
                        dwc3_suspend_gadget(dwc);
@@ -2934,7 +3305,17 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
        case DWC3_LINK_STATE_U3:
                dwc3_suspend_gadget(dwc);
                break;
+       case DWC3_LINK_STATE_RX_DET:    /* Early Suspend in HS */
+               if (dwc->is_not_vbus_pad) {
+                       phy_set(dwc->usb2_generic_phy, SET_DPPULLUP_ENABLE, NULL);
+                       phy_set(dwc->usb3_generic_phy, SET_DPPULLUP_ENABLE, NULL);
+               }
+               break;
        case DWC3_LINK_STATE_RESUME:
+               if (dwc->is_not_vbus_pad) {
+                       phy_set(dwc->usb2_generic_phy, SET_DPPULLUP_ENABLE, NULL);
+                       phy_set(dwc->usb3_generic_phy, SET_DPPULLUP_ENABLE, NULL);
+               }
                dwc3_resume_gadget(dwc);
                break;
        default:
@@ -2950,6 +3331,10 @@ static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc,
 {
        enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
 
+       /* WA for Lhotse U3 */
+       if (dwc->gadget.speed >= USB_SPEED_SUPER)
+               phy_ilbk(dwc->usb3_generic_phy);
+
        if (dwc->link_state != next && next == DWC3_LINK_STATE_U3)
                dwc3_suspend_gadget(dwc);
 
@@ -3048,15 +3433,22 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
        int left;
        u32 reg;
 
+#if IS_ENABLED(DWC3_GADGET_IRQ_ORG)
        left = evt->count;
 
        if (!(evt->flags & DWC3_EVENT_PENDING))
                return IRQ_NONE;
+#else
+       reg = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
+       reg &= DWC3_GEVNTCOUNT_MASK;
+       evt->count = reg;
+       left = evt->count;
+#endif
 
        while (left > 0) {
                union dwc3_event event;
 
-               event.raw = *(u32 *) (evt->cache + evt->lpos);
+               event.raw = *(u32 *) (evt->buf + evt->lpos);
 
                dwc3_process_event_entry(dwc, &event);
 
@@ -3069,18 +3461,24 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
                 * boundary so I worry about that once we try to handle
                 * that.
                 */
-               evt->lpos = (evt->lpos + 4) % evt->length;
+               evt->lpos = (evt->lpos + 4) % DWC3_EVENT_BUFFERS_SIZE;
                left -= 4;
+
+               dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 4);
        }
 
        evt->count = 0;
+#if IS_ENABLED(DWC3_GADGET_IRQ_ORG)
        evt->flags &= ~DWC3_EVENT_PENDING;
+#endif
        ret = IRQ_HANDLED;
 
+#if IS_ENABLED(DWC3_GADGET_IRQ_ORG)
        /* Unmask interrupt */
        reg = dwc3_readl(dwc->regs, DWC3_GEVNTSIZ(0));
        reg &= ~DWC3_GEVNTSIZ_INTMASK;
        dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), reg);
+#endif
 
        if (dwc->imod_interval) {
                dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), DWC3_GEVNTCOUNT_EHB);
@@ -3090,6 +3488,7 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
        return ret;
 }
 
+#if IS_ENABLED(DWC3_GADGET_IRQ_ORG)
 static irqreturn_t dwc3_thread_interrupt(int irq, void *_evt)
 {
        struct dwc3_event_buffer *evt = _evt;
@@ -3150,12 +3549,29 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
 
        return IRQ_WAKE_THREAD;
 }
+#endif
 
 static irqreturn_t dwc3_interrupt(int irq, void *_evt)
 {
        struct dwc3_event_buffer        *evt = _evt;
+       irqreturn_t                     ret = IRQ_NONE;
+       struct dwc3 *dwc = evt->dwc;
+
+       spin_lock(&dwc->lock);
+
+#if IS_ENABLED(DWC3_GADGET_IRQ_ORG)
+       irqreturn_t status;
 
-       return dwc3_check_event_buf(evt);
+       status = dwc3_check_event_buf(evt);
+       if (status == IRQ_WAKE_THREAD)
+               ret = status;
+#else
+       ret |= dwc3_process_event_buf(evt);
+#endif
+
+       spin_unlock(&dwc->lock);
+
+       return ret;
 }
 
 static int dwc3_gadget_get_irq(struct dwc3 *dwc)
@@ -3277,9 +3693,18 @@ int dwc3_gadget_init(struct dwc3 *dwc)
                goto err4;
        }
 
+       if (dwc->dotg) {
+               ret = otg_set_peripheral(&dwc->dotg->otg, &dwc->gadget);
+               if (ret) {
+                       dev_err(dwc->dev, "failed to set otg peripheral\n");
+                       goto err4;
+               }
+       }
+
        return 0;
 
 err4:
+       usb_del_gadget_udc(&dwc->gadget);
        dwc3_gadget_free_endpoints(dwc);
 
 err3:
@@ -3301,6 +3726,9 @@ err0:
 
 void dwc3_gadget_exit(struct dwc3 *dwc)
 {
+       if (dwc->dotg)
+               otg_set_peripheral(&dwc->dotg->otg, NULL);
+
        usb_del_gadget_udc(&dwc->gadget);
        dwc3_gadget_free_endpoints(dwc);
        dma_free_coherent(dwc->sysdev, DWC3_BOUNCE_SIZE, dwc->bounce,
@@ -3346,6 +3774,28 @@ err0:
        return ret;
 }
 
+void dwc3_gadget_disconnect_proc(struct dwc3 *dwc)
+{
+       int                     reg;
+
+       reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+       reg &= ~DWC3_DCTL_INITU1ENA;
+       dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+       reg &= ~DWC3_DCTL_INITU2ENA;
+       dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+
+       if (dwc->gadget_driver && dwc->gadget_driver->disconnect)
+               dwc->gadget_driver->disconnect(&dwc->gadget);
+
+       dwc->start_config_issued = false;
+
+       dwc->gadget.speed = USB_SPEED_UNKNOWN;
+       dwc->setup_packet_pending = false;
+
+       complete(&dwc->disconnect);
+}
+
 void dwc3_gadget_process_pending_events(struct dwc3 *dwc)
 {
        if (dwc->pending_events) {
index 4a3227543255e33a805b282f4bc126d84fc7acdd..415c754b934e1b8765d0d8f354212ef69d5102f2 100644 (file)
@@ -115,4 +115,10 @@ static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
        return DWC3_DEPCMD_GET_RSC_IDX(res_id);
 }
 
+/**
+ * ISR for DWC3 gadget was changed because of RNDIS performance.
+ * However, previous ISR code was not removed to track the history.
+ */
+#undef DWC3_GADGET_IRQ_ORG
+
 #endif /* __DRIVERS_USB_DWC3_GADGET_H */
index 76f0b0df37c13c4cc5b4fbe87b21c0f7d7f5cc5a..903d0962d1b2be450e7bd320435f3021a9975f0f 100644 (file)
  */
 
 #include <linux/platform_device.h>
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+#include <linux/usb/exynos_usb_audio.h>
+#endif
 
 #include "core.h"
 
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+struct host_data xhci_data;
+#endif
+
 static int dwc3_host_get_irq(struct dwc3 *dwc)
 {
        struct platform_device  *dwc3_pdev = to_platform_device(dwc->dev);
@@ -60,6 +67,9 @@ int dwc3_host_init(struct dwc3 *dwc)
        struct resource         *res;
        struct platform_device  *dwc3_pdev = to_platform_device(dwc->dev);
        int                     prop_idx = 0;
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+       dma_addr_t      dma;
+#endif
 
        irq = dwc3_host_get_irq(dwc);
        if (irq < 0)
@@ -86,6 +96,11 @@ int dwc3_host_init(struct dwc3 *dwc)
        }
 
        xhci->dev.parent        = dwc->dev;
+       //xhci->dev.dma_mask    = dwc->dev->dma_mask;
+       //xhci->dev.dma_parms   = dwc->dev->dma_parms;
+       //xhci->dev.archdata.dma_ops = dwc->dev->archdata.dma_ops;
+
+       dma_set_coherent_mask(&xhci->dev, dwc->dev->coherent_dma_mask);
 
        dwc->xhci = xhci;
 
@@ -126,13 +141,23 @@ int dwc3_host_init(struct dwc3 *dwc)
        phy_create_lookup(dwc->usb3_generic_phy, "usb3-phy",
                          dev_name(dwc->dev));
 
-       ret = platform_device_add(xhci);
-       if (ret) {
-               dev_err(dwc->dev, "failed to register xHCI device\n");
-               goto err2;
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+       xhci_data.in_data_addr = dma_alloc_coherent(dwc->dev, (PAGE_SIZE * 256), &dma,
+                       GFP_KERNEL);
+       xhci_data.in_data_dma = dma;
+       dev_info(dwc->dev, "// Data address = 0x%llx (DMA), %p (virt)",
+               (unsigned long long)xhci_data.in_data_dma, xhci_data.in_data_addr);
+#endif
+       if (!dwc->dotg) {
+               ret = platform_device_add(xhci);
+               if (ret) {
+                       dev_err(dwc->dev, "failed to register xHCI device\n");
+                       goto err2;
+               }
        }
 
        return 0;
+
 err2:
        phy_remove_lookup(dwc->usb2_generic_phy, "usb2-phy",
                          dev_name(dwc->dev));
@@ -149,5 +174,6 @@ void dwc3_host_exit(struct dwc3 *dwc)
                          dev_name(dwc->dev));
        phy_remove_lookup(dwc->usb3_generic_phy, "usb3-phy",
                          dev_name(dwc->dev));
-       platform_device_unregister(dwc->xhci);
+       if (!dwc->dotg)
+               platform_device_unregister(dwc->xhci);
 }
index f572b645d21b8a1a20a9dc59abc47fef1dc20bf4..e07ee956bfb16dfc986210e932f00e487ed8ddc5 100644 (file)
@@ -215,6 +215,12 @@ config USB_F_PRINTER
 config USB_F_TCM
        tristate
 
+config USB_F_MTP
+       tristate
+
+config USB_F_PTP
+        tristate
+
 config USB_F_AUDIO_SRC
        tristate
 
@@ -374,6 +380,34 @@ config USB_CONFIGFS_F_FS
          implemented in kernel space (for instance Ethernet, serial or
          mass storage) and other are implemented in user space.
 
+config USB_CONFIGFS_F_DM
+        boolean "DM gadget"
+        depends on USB_CONFIGFS
+        select USB_F_DM
+        help
+          USB gadget DM(Diagnosis Monitor) support
+
+config USB_CONFIGFS_F_ADB
+       boolean "ADB gadget"
+       depends on USB_CONFIGFS
+       select USB_F_ADB
+       help
+         USB gadget ADB support
+
+config USB_CONFIGFS_F_MTP
+       boolean "MTP gadget"
+       depends on USB_CONFIGFS
+       select USB_F_MTP
+       help
+         USB gadget MTP support
+
+config USB_CONFIGFS_F_PTP
+       boolean "PTP gadget"
+       depends on USB_CONFIGFS && USB_CONFIGFS_F_MTP
+       select USB_F_PTP
+       help
+         USB gadget PTP support
+
 config USB_CONFIGFS_F_ACC
        boolean "Accessory gadget"
        depends on USB_CONFIGFS
index bf2d0ce80c99af5c4b113135e62a553d1b919311..8c8423885827916eb717065c7c45157546a60292 100644 (file)
@@ -8,6 +8,7 @@
 #include "configfs.h"
 #include "u_f.h"
 #include "u_os_desc.h"
+#include <linux/soc/samsung/exynos-soc.h>
 
 #ifdef CONFIG_USB_CONFIGFS_UEVENT
 #include <linux/platform_device.h>
@@ -34,6 +35,13 @@ struct device *create_function_device(char *name)
 EXPORT_SYMBOL_GPL(create_function_device);
 #endif
 
+#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
+void set_usb_enumeration_state(bool state);
+void set_usb_enable_state(void);
+#endif
+
+#define CHIPID_SIZE    (16)
+
 int check_user_usb_string(const char *name,
                struct usb_gadget_strings *stringtab_dev)
 {
@@ -86,10 +94,12 @@ struct gadget_info {
        char b_vendor_code;
        char qw_sign[OS_STRING_QW_SIGN_LEN];
 #ifdef CONFIG_USB_CONFIGFS_UEVENT
+       bool enabled;
        bool connected;
        bool sw_connected;
        struct work_struct work;
        struct device *dev;
+       struct list_head linked_func;
 #endif
 };
 
@@ -156,6 +166,27 @@ static int usb_string_copy(const char *s, char **s_copy)
        return 0;
 }
 
+static int set_alt_serialnumber(struct gadget_strings *gs)
+{
+       char *str;
+       int ret = -ENOMEM;
+
+       str = kmalloc(CHIPID_SIZE + 1, GFP_KERNEL);
+       if (!str) {
+               pr_err("%s: failed to alloc for string\n", __func__);
+               return ret;
+       }
+
+       snprintf(str, CHIPID_SIZE + 1, "%016lx", (long)exynos_soc_info.unique_id);
+       if (usb_string_copy(str, &gs->serialnumber))
+               pr_err("%s: failed to copy alternative string\n", __func__);
+       else
+               ret = 0;
+
+       kfree(str);
+       return ret;
+}
+
 #define GI_DEVICE_DESC_SIMPLE_R_u8(__name)     \
 static ssize_t gadget_dev_desc_##__name##_show(struct config_item *item, \
                        char *page)     \
@@ -288,6 +319,8 @@ static ssize_t gadget_dev_desc_UDC_store(struct config_item *item,
        char *name;
        int ret;
 
+       pr_info("%s: +++\n", __func__);
+
        name = kstrdup(page, GFP_KERNEL);
        if (!name)
                return -ENOMEM;
@@ -432,8 +465,12 @@ static int config_usb_cfg_link(
                goto out;
        }
 
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+       list_add_tail(&f->list, &gi->linked_func);
+#else
        /* stash the function until we bind it to the gadget */
        list_add_tail(&f->list, &cfg->func_list);
+#endif
        ret = 0;
 out:
        mutex_unlock(&gi->lock);
@@ -1246,7 +1283,7 @@ static void purge_configs_funcs(struct gadget_info *gi)
 
                        list_move_tail(&f->list, &cfg->func_list);
                        if (f->unbind) {
-                               dev_dbg(&gi->cdev.gadget->dev,
+                               dev_err(&gi->cdev.gadget->dev,
                                         "unbind function '%s'/%p\n",
                                         f->name, f);
                                f->unbind(c, f);
@@ -1313,6 +1350,9 @@ static int configfs_composite_bind(struct usb_gadget *gadget,
                        gs->strings[USB_GADGET_MANUFACTURER_IDX].s =
                                gs->manufacturer;
                        gs->strings[USB_GADGET_PRODUCT_IDX].s = gs->product;
+                       if (gs->serialnumber && !set_alt_serialnumber(gs))
+                               pr_info("usb: serial number: %s\n",
+                                               gs->serialnumber);
                        gs->strings[USB_GADGET_SERIAL_IDX].s = gs->serialnumber;
                        i++;
                }
@@ -1433,6 +1473,9 @@ static void android_work(struct work_struct *data)
                                        KOBJ_CHANGE, connected);
                pr_info("%s: sent uevent %s\n", __func__, connected[0]);
                uevent_sent = true;
+#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
+               set_usb_enumeration_state(true);
+#endif
        }
 
        if (status[1]) {
@@ -1484,6 +1527,8 @@ static int android_setup(struct usb_gadget *gadget,
        struct gadget_info *gi = container_of(cdev, struct gadget_info, cdev);
        int value = -EOPNOTSUPP;
        struct usb_function_instance *fi;
+       struct usb_configuration *configuration;
+       struct usb_function *f;
 
        spin_lock_irqsave(&cdev->lock, flags);
        if (!gi->connected) {
@@ -1498,6 +1543,22 @@ static int android_setup(struct usb_gadget *gadget,
                                break;
                }
        }
+       list_for_each_entry(configuration, &cdev->configs, list) {
+               list_for_each_entry(f, &configuration->functions, list) {
+                       if (f != NULL && f->ctrlrequest != NULL) {
+                               value = f->ctrlrequest(f, c);
+                               if (value >= 0)
+                                       break;
+                       }
+               }
+       }
+
+/*        //todo
+#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE
+       if (value < 0)
+               value = terminal_ctrl_request(cdev, c);
+#endif
+*/
 
 #ifdef CONFIG_USB_CONFIGFS_F_ACC
        if (value < 0)
@@ -1508,6 +1569,13 @@ static int android_setup(struct usb_gadget *gadget,
                value = composite_setup(gadget, c);
 
        spin_lock_irqsave(&cdev->lock, flags);
+
+#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE
+       if (c->bRequest == USB_REQ_SET_CONFIGURATION &&
+                       cdev->mute_switch == true)
+               cdev->mute_switch = false;
+#endif
+
        if (c->bRequest == USB_REQ_SET_CONFIGURATION &&
                                                cdev->config) {
                schedule_work(&gi->work);
@@ -1543,8 +1611,35 @@ static void android_disconnect(struct usb_gadget *gadget)
        acc_disconnect();
 #endif
        gi->connected = 0;
+
+#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE
+       printk(KERN_DEBUG "usb: %s con(%d), sw(%d)\n",
+                __func__, gi->connected, gi->sw_connected);
+       /* avoid sending a disconnect switch event
+        * until after we disconnect.
+        */
+       if (cdev->mute_switch) {
+               gi->sw_connected = gi->connected;
+               printk(KERN_DEBUG"usb: %s mute_switch con(%d) sw(%d)\n",
+                        __func__, gi->connected, gi->sw_connected);
+       } else {
+
+       //      set_ncm_ready(false);
+               if (cdev->force_disconnect) {
+                       gi->sw_connected = 1;
+                       printk(KERN_DEBUG"usb: %s force_disconnect\n",
+                                __func__);
+                       cdev->force_disconnect = 0;
+               }
+               printk(KERN_DEBUG"usb: %s schedule_work con(%d) sw(%d)\n",
+                        __func__, gi->connected, gi->sw_connected);
+               schedule_work(&gi->work);
+       }
+       composite_disconnect(gadget);
+#else
        schedule_work(&gi->work);
        composite_disconnect(gadget);
+#endif
 }
 #endif
 
@@ -1572,6 +1667,160 @@ static const struct usb_gadget_driver configfs_driver_template = {
 };
 
 #ifdef CONFIG_USB_CONFIGFS_UEVENT
+static ssize_t
+functions_show(struct device *pdev, struct device_attribute *attr, char *buf)
+{
+       struct gadget_info *dev = dev_get_drvdata(pdev);
+       struct usb_composite_dev *cdev;
+       struct usb_configuration *c;
+       struct usb_function *f;
+       char *buff = buf;
+
+       cdev = &dev->cdev;
+       if (!cdev)
+               return -ENODEV;
+
+       mutex_lock(&dev->lock);
+
+       list_for_each_entry(c, &cdev->configs, list) {
+               list_for_each_entry(f, &c->functions, list) {
+                       buff += sprintf(buff, "%s,", f->name);
+               }
+       }
+
+       mutex_unlock(&dev->lock);
+
+       if (buff != buf)
+               *(buff-1) = '\n';
+
+       return buff - buf;
+}
+
+static ssize_t
+functions_store(struct device *pdev, struct device_attribute *attr,
+                                       const char *buff, size_t size)
+{
+       struct gadget_info *dev = dev_get_drvdata(pdev);
+       struct usb_composite_dev *cdev;
+       struct usb_configuration *c;
+       struct config_usb_cfg *cfg;
+       struct usb_function *f, *tmp;
+       char *name;
+       char buf[256], *b;
+
+       cdev = &dev->cdev;
+       if (!cdev)
+               return -ENODEV;
+
+       mutex_lock(&dev->lock);
+
+       if (dev->enabled) {
+               mutex_unlock(&dev->lock);
+               pr_err("%s: gadget is enabled\n", __func__);
+               return -EBUSY;
+       }
+
+       strlcpy(buf, buff, sizeof(buf));
+       b = strim(buf);
+
+       printk("Function stored : %s\n", b);
+       while (b) {
+               name = strsep(&b, ",");
+               if (!name)
+                       continue;
+
+               list_for_each_entry(c, &cdev->configs, list) {
+                       cfg = container_of(c, struct config_usb_cfg, c);
+                       list_for_each_entry_safe(f, tmp, &dev->linked_func, list) {
+                               if (!strcmp(f->name, name)) {
+                                       pr_err("%s: enable device[%s]\n", __func__, name);
+                                       list_move_tail(&f->list, &cfg->func_list);
+                               }
+                       }
+               }
+       }
+
+       mutex_unlock(&dev->lock);
+
+       return size;
+}
+
+static ssize_t enable_show(struct device *pdev, struct device_attribute *attr,
+                          char *buf)
+{
+       struct gadget_info *dev = dev_get_drvdata(pdev);
+       return sprintf(buf, "%d\n", dev->enabled);
+}
+
+static ssize_t enable_store(struct device *pdev, struct device_attribute *attr,
+                           const char *buff, size_t size)
+{
+       struct gadget_info *dev = dev_get_drvdata(pdev);
+       struct usb_composite_dev *cdev;
+       struct usb_gadget *gadget;
+       struct usb_configuration *c;
+       struct config_usb_cfg *cfg;
+       struct usb_function *f, *tmp;
+       int enabled = 0;
+
+       if (!dev)
+               return -ENODEV;
+
+       cdev = &dev->cdev;
+       if (!cdev)
+               return -ENODEV;
+
+       gadget = cdev->gadget;
+       mutex_lock(&dev->lock);
+
+       sscanf(buff, "%d", &enabled);
+       if (enabled && !dev->enabled) {
+               pr_info("%s: Connect gadget: enabled=%d, dev->enabled=%d\n",
+                               __func__, enabled, dev->enabled);
+#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE
+               cdev->next_string_id = 4;  //composite string index
+#else
+               cdev->next_string_id = 0;
+#endif
+               if (!gadget) {
+                       pr_info("%s: Gadget is NULL: %p\n", __func__, gadget);
+                       mutex_unlock(&dev->lock);
+                       return -ENODEV;
+               }
+
+               usb_gadget_connect(gadget);
+               dev->enabled = true;
+#ifdef CONFIG_USB_TYPEC_MANAGER_NOTIFIER
+               set_usb_enable_state();
+#endif
+       } else if (!enabled && dev->enabled) {
+               pr_info("%s: Disconnect gadget: enabled=%d, dev->enabled=%d\n",
+                               __func__, enabled, dev->enabled);
+#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE
+               /* avoid sending a disconnect switch event
+                * until after we disconnect.
+                */
+               cdev->mute_switch = true;
+#endif
+               unregister_gadget(dev);
+               list_for_each_entry(c, &cdev->configs, list) {
+                       cfg = container_of(c, struct config_usb_cfg, c);
+                       list_for_each_entry_safe(f, tmp, &cfg->func_list, list) {
+                               list_move_tail(&f->list, &dev->linked_func);
+                       }
+                       c->next_interface_id = 0;
+                       memset(c->interface, 0, sizeof(c->interface));
+               }
+               dev->enabled = false;
+       } else {
+               pr_err("%s: already %s\n", __func__,
+                               dev->enabled ? "enabled" : "disabled");
+       }
+
+       mutex_unlock(&dev->lock);
+       return size;
+}
+
 static ssize_t state_show(struct device *pdev, struct device_attribute *attr,
                        char *buf)
 {
@@ -1598,10 +1847,30 @@ out:
        return sprintf(buf, "%s\n", state);
 }
 
+static DEVICE_ATTR(functions, S_IRUGO | S_IWUSR, functions_show,
+                                               functions_store);
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store);
 static DEVICE_ATTR(state, S_IRUGO, state_show, NULL);
 
+#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE
+static ssize_t
+bcdUSB_show(struct device *pdev, struct device_attribute *attr, char *buf)
+{
+       struct gadget_info *dev = dev_get_drvdata(pdev);
+
+       return sprintf(buf, "%04x\n", dev->cdev.desc.bcdUSB);
+
+}
+static DEVICE_ATTR(bcdUSB, S_IRUGO, bcdUSB_show, NULL);
+#endif
+
 static struct device_attribute *android_usb_attributes[] = {
        &dev_attr_state,
+       &dev_attr_enable,
+       &dev_attr_functions,
+#ifdef CONFIG_USB_ANDROID_SAMSUNG_COMPOSITE
+       &dev_attr_bcdUSB,
+#endif
        NULL
 };
 
@@ -1691,6 +1960,9 @@ static struct config_group *gadgets_make(
        mutex_init(&gi->lock);
        INIT_LIST_HEAD(&gi->string_list);
        INIT_LIST_HEAD(&gi->available_func);
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+       INIT_LIST_HEAD(&gi->linked_func);
+#endif
 
        composite_init_dev(&gi->cdev);
        gi->cdev.desc.bLength = USB_DT_DEVICE_SIZE;
index d7d5673d834393f6deefc6eb22da0581d7f26ddd..c9af6c1de939540fcd8a78b004cb8a892722d06c 100644 (file)
@@ -9,6 +9,8 @@ ccflags-y                       += -I$(srctree)/drivers/usb/gadget/udc/
 # USB Functions
 usb_f_acm-y                    := f_acm.o
 obj-$(CONFIG_USB_F_ACM)                += usb_f_acm.o
+usb_f_uts-y                    := f_uts.o
+obj-$(CONFIG_USB_F_ACM)                += usb_f_uts.o
 usb_f_ss_lb-y                  := f_loopback.o f_sourcesink.o
 obj-$(CONFIG_USB_F_SS_LB)      += usb_f_ss_lb.o
 obj-$(CONFIG_USB_U_SERIAL)     += u_serial.o
@@ -48,8 +50,16 @@ usb_f_hid-y                  := f_hid.o
 obj-$(CONFIG_USB_F_HID)                += usb_f_hid.o
 usb_f_printer-y                        := f_printer.o
 obj-$(CONFIG_USB_F_PRINTER)    += usb_f_printer.o
+usb_f_dm-y                     := f_dm.o
+obj-$(CONFIG_USB_CONFIGFS_F_DM)        += usb_f_dm.o
+usb_f_adb-y                    := f_adb.o
+obj-$(CONFIG_USB_CONFIGFS_F_ADB)+= usb_f_adb.o
 usb_f_tcm-y                    := f_tcm.o
 obj-$(CONFIG_USB_F_TCM)                += usb_f_tcm.o
+usb_f_mtp-y                     := f_mtp.o
+obj-$(CONFIG_USB_F_MTP)         += usb_f_mtp.o
+usb_f_ptp-y                     := f_ptp.o
+obj-$(CONFIG_USB_F_PTP)         += usb_f_ptp.o
 usb_f_audio_source-y            := f_audio_source.o
 obj-$(CONFIG_USB_F_AUDIO_SRC)   += usb_f_audio_source.o
 usb_f_accessory-y               := f_accessory.o
index 0904cb6ce4def43fb65139a104927c334cb520fc..0366af6a7546087783c8c6aa7acb0fb267df883b 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/mmu_context.h>
 #include <linux/poll.h>
 #include <linux/eventfd.h>
+#include <linux/delay.h>
 
 #include "u_fs.h"
 #include "u_f.h"
@@ -1010,9 +1011,16 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
 
                if (interrupted)
                        ret = -EINTR;
-               else if (io_data->read && ep->status > 0)
+               else if (io_data->read && ep->status > 0) {
+                       if (ep->status > 0xFFFFFF) {
+                               pr_info("%s abnormal size=%d\n",
+                                               __func__, (int)ep->status);
+                               ret = -ENOMEM;
+                               goto error_mutex;
+                       }
                        ret = __ffs_epfile_read_data(epfile, data, ep->status,
                                                     &io_data->data);
+               }
                else
                        ret = ep->status;
                goto error_mutex;
@@ -2908,6 +2916,7 @@ static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
        struct f_fs_opts *ffs_opts =
                container_of(f->fi, struct f_fs_opts, func_inst);
        int ret;
+       int retries = 500;
 
        ENTER();
 
@@ -2918,12 +2927,21 @@ static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
         *
         * Configfs-enabled gadgets however do need ffs_dev_lock.
         */
-       if (!ffs_opts->no_configfs)
-               ffs_dev_lock();
-       ret = ffs_opts->dev->desc_ready ? 0 : -ENODEV;
-       func->ffs = ffs_opts->dev->ffs_data;
-       if (!ffs_opts->no_configfs)
-               ffs_dev_unlock();
+       do {
+               if (!ffs_opts->no_configfs)
+                       ffs_dev_lock();
+               ret = ffs_opts->dev->desc_ready ? 0 : -ENODEV;
+               func->ffs = ffs_opts->dev->ffs_data;
+               if (!ffs_opts->no_configfs)
+                       ffs_dev_unlock();
+               if (ret)
+                       msleep(20);
+               else
+                       break;
+       } while (--retries);
+
+       pr_info("ffs_do_functionfs_bind %d %d\n", ret, retries);
+
        if (ret)
                return ERR_PTR(ret);
 
@@ -3477,7 +3495,7 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
        if (unlikely(!func))
                return ERR_PTR(-ENOMEM);
 
-       func->function.name    = "Function FS Gadget";
+       func->function.name    = "adb";
 
        func->function.bind    = ffs_func_bind;
        func->function.unbind  = ffs_func_unbind;
index c7c5b3ce1d98840d8d4e9122d60fd51ae75b330d..4723ba2ece9b7e6fa35df98cce223a6c4829c953 100644 (file)
@@ -926,7 +926,7 @@ static struct usb_function_instance *rndis_alloc_inst(void)
 
        mutex_init(&opts->lock);
        opts->func_inst.free_func_inst = rndis_free_inst;
-       opts->net = gether_setup_default();
+       opts->net = gether_setup_name_default("rndis");
        if (IS_ERR(opts->net)) {
                struct net_device *net = opts->net;
                kfree(opts);
@@ -971,6 +971,10 @@ static void rndis_free(struct usb_function *f)
 static void rndis_unbind(struct usb_configuration *c, struct usb_function *f)
 {
        struct f_rndis          *rndis = func_to_rndis(f);
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+       struct f_rndis_opts     *opts;
+       struct usb_composite_dev *cdev = f->config->cdev;
+#endif
 
        kfree(f->os_desc_table);
        f->os_desc_n = 0;
@@ -978,6 +982,27 @@ static void rndis_unbind(struct usb_configuration *c, struct usb_function *f)
 
        kfree(rndis->notify_req->buf);
        usb_ep_free_request(rndis->notify, rndis->notify_req);
+
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+       opts = container_of(f->fi, struct f_rndis_opts, func_inst);
+       if (!opts->borrowed_net) {
+               if (opts->bound)
+                       gether_cleanup(netdev_priv(opts->net));
+               else
+                       free_netdev(opts->net);
+       }
+
+       opts->net = gether_setup_name_default("rndis");
+       if (IS_ERR(opts->net)) {
+               ERROR(cdev, "%s: failed to setup ethernet\n", f->name);
+               return;
+       }
+
+       gether_get_host_addr_u8(opts->net, rndis->ethaddr);
+       rndis->port.ioport = netdev_priv(opts->net);
+
+       opts->bound = false;
+#endif
 }
 
 static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
index 4176216d54be7589a1a84b2d39ce7d803c4d3299..33373d2e70dbfee8ddfe7c79c739a0939dc5c80c 100644 (file)
@@ -1371,7 +1371,12 @@ int gserial_alloc_line(unsigned char *line_num)
        coding.bParityType = USB_CDC_NO_PARITY;
        coding.bDataBits = USB_CDC_1_STOP_BITS;
 
-       for (port_num = 0; port_num < MAX_U_SERIAL_PORTS; port_num++) {
+       if (*line_num)
+               port_num =  *line_num;
+       else
+               port_num = 0;
+
+       for (; port_num < MAX_U_SERIAL_PORTS; port_num++) {
                ret = gs_port_alloc(port_num, &coding);
                if (ret == -EBUSY)
                        continue;
index 37ef2ac9cdae02aa81df2b0436bcbb257e0ec362..460ea13d502d5428d2be94110f25975de2594e10 100644 (file)
@@ -590,6 +590,19 @@ static int check_reset_complete (
                        return port_status;
                }
 
+               /* W/A for Synopsys HC HSIC port.
+                * Return at this point to prevent port owner change
+                * and retry port reset.
+                */
+               if (ehci->has_synopsys_hsic_bug) {
+                       if ((index + 1) == ehci->hsic_ports) {
+                               ehci_err (ehci,
+                                       "Failed to enable HSIC port %d\n",
+                                       index + 1);
+                               return port_status;
+                       }
+               }
+
                ehci_dbg (ehci, "port %d full speed --> companion\n",
                        index + 1);
 
@@ -1238,6 +1251,20 @@ int ehci_hub_control(
                                        wIndex + 1);
                                temp |= PORT_OWNER;
                        } else {
+                               ehci_vdbg (ehci, "port %d reset\n", wIndex + 1);
+
+                               /* W/A for Synopsys HC HSIC port.
+                                * Disable HSIC port to prevent
+                                * the port reset failure.
+                                */
+                               if (ehci->has_synopsys_hsic_bug) {
+                                       if ((wIndex + 1) == ehci->hsic_ports) {
+                                               ehci_writel(ehci,
+                                                       temp & ~PORT_PE,
+                                                       status_reg);
+                                       }
+                               }
+
                                temp |= PORT_RESET;
                                temp &= ~PORT_PE;
 
index a8e36170d8b8a623e4726265b4d194156f231d4f..20bcb3bf27c91e348f28f9c6213288b2612c4834 100644 (file)
@@ -203,6 +203,8 @@ struct ehci_hcd {                   /* one per controller */
                        suspended */
        unsigned long           resuming_ports;         /* which ports have
                        started to resume */
+       unsigned long           hsic_ports;             /* which ports are
+                       used for HSIC */
 
        /* per-HC memory pools (could be per-bus, but ...) */
        struct dma_pool         *qh_pool;       /* qh per active urb */
@@ -228,6 +230,7 @@ struct ehci_hcd {                   /* one per controller */
        unsigned                amd_pll_fix:1;
        unsigned                use_dummy_qh:1; /* AMD Frame List table quirk*/
        unsigned                has_synopsys_hc_bug:1; /* Synopsys HC */
+       unsigned                has_synopsys_hsic_bug:1; /* Synop HSIC port */
        unsigned                frame_index_bug:1; /* MosChip (AKA NetMos) */
        unsigned                need_oc_pp_cycle:1; /* MPC834X port power */
        unsigned                imx28_write_fix:1; /* For Freescale i.MX28 */
index 9762333d8d7fc1a5f469545aa5800967fb99863b..191dda5924751201ab20eb0bb10f3ab2bd40cfc8 100644 (file)
@@ -1175,6 +1175,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 
                        temp = readl(port_array[wIndex]);
                        bus_state->suspended_ports |= 1 << wIndex;
+
+                       /* WA for Lhotse U3 Suspend */
+                       if (xhci_hub_check_speed(hcd))
+                               phy_ilbk(xhci->main_hcd->phy);
+
                        break;
                case USB_PORT_FEAT_LINK_STATE:
                        temp = readl(port_array[wIndex]);
@@ -1472,6 +1477,40 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
        return status ? retval : 0;
 }
 
+int xhci_hub_check_speed(struct usb_hcd *hcd)
+{
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       int slot_id;
+       int i;
+       enum usb_device_speed speed;
+
+       if (hcd->speed < HCD_USB3)
+               return 0;
+
+       slot_id = 0;
+       for (i = 0; i < MAX_HC_SLOTS; i++) {
+               if (!xhci->devs[i])
+                       continue;
+               speed = xhci->devs[i]->udev->speed;
+               if (speed >= USB_SPEED_SUPER) {
+                       return 1;
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+int xhci_check_usbl2_support(struct usb_hcd *hcd)
+{
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+       if (xhci->quirks & XHCI_L2_SUPPORT)
+               return true;
+       else
+               return false;
+}
+
 #ifdef CONFIG_PM
 
 int xhci_bus_suspend(struct usb_hcd *hcd)
@@ -1481,6 +1520,8 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
        __le32 __iomem **port_array;
        struct xhci_bus_state *bus_state;
        unsigned long flags;
+       int is_port_connect = 0;
+       int ret;
 
        max_ports = xhci_get_ports(hcd, &port_array);
        bus_state = &xhci->bus_state[hcd_index(hcd)];
@@ -1507,7 +1548,6 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
                t2 = xhci_port_state_to_neutral(t1);
 
                if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) {
-                       xhci_dbg(xhci, "port %d not suspended\n", port_index);
                        slot_id = xhci_find_slot_id_by_port(hcd, xhci,
                                        port_index + 1);
                        if (slot_id) {
@@ -1527,20 +1567,38 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
                        if (t1 & PORT_CONNECT) {
                                t2 |= PORT_WKOC_E | PORT_WKDISC_E;
                                t2 &= ~PORT_WKCONN_E;
+                               is_port_connect = 1;
                        } else {
                                t2 |= PORT_WKOC_E | PORT_WKCONN_E;
                                t2 &= ~PORT_WKDISC_E;
                        }
-               } else
+               } else {
                        t2 &= ~PORT_WAKE_BITS;
+               }
 
                t1 = xhci_port_state_to_neutral(t1);
-               if (t1 != t2)
+               if (t1 != t2) {
                        writel(t2, port_array[port_index]);
+               }
        }
+
+       if (is_port_connect && usb_hcd_is_primary_hcd(hcd)) {
+               xhci_info(xhci, "port is connected, phy vendor set\n");
+               ret = phy_vendor_set(xhci->main_hcd->phy, 1, 0);
+               if (ret) {
+                       xhci_info(xhci, "phy vendor set fail\n");
+                       spin_unlock_irqrestore(&xhci->lock, flags);
+                       return ret;
+               }
+       }
+
+       xhci_info(xhci, "%s 'HC_STATE_SUSPENDED' portcon: %d primary_hcd: %d\n",
+               __func__, is_port_connect, usb_hcd_is_primary_hcd(hcd));
        hcd->state = HC_STATE_SUSPENDED;
        bus_state->next_statechange = jiffies + msecs_to_jiffies(10);
+
        spin_unlock_irqrestore(&xhci->lock, flags);
+
        return 0;
 }
 
@@ -1585,6 +1643,10 @@ int xhci_bus_resume(struct usb_hcd *hcd)
        u32 next_state;
        u32 temp, portsc;
 
+       if (usb_hcd_is_primary_hcd(hcd)) {
+               xhci_info(xhci, "[%s] phy vendor set \n",__func__);
+               phy_vendor_set(xhci->main_hcd->phy, 1, 1);
+       }
        max_ports = xhci_get_ports(hcd, &port_array);
        bus_state = &xhci->bus_state[hcd_index(hcd)];
 
@@ -1683,6 +1745,10 @@ int xhci_bus_resume(struct usb_hcd *hcd)
        temp = readl(&xhci->op_regs->command);
 
        spin_unlock_irqrestore(&xhci->lock, flags);
+
+       hcd->state = HC_STATE_RESUMING;
+       xhci_info(xhci, "%s is done, hcd state is 'HC_STATE_RESUMING'\n",__func__);
+
        return 0;
 }
 
index ccdc971283d092189eb52c3a746fcf107180454b..1df84e2b16c60f2ae31fc5b9e92a6450e8781632 100644 (file)
@@ -1796,6 +1796,24 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
        xhci->event_ring = NULL;
        xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed event ring");
 
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+       if (xhci->save_addr)
+               dma_free_coherent(dev, sizeof(PAGE_SIZE), xhci->save_addr, xhci->save_dma);
+       xhci_info(xhci, "%s: Freed save-restore buffer for Audio offloading", __func__);
+
+       size = sizeof(struct xhci_erst_entry)*(xhci->erst_audio.num_entries);
+       if (xhci->erst_audio.entries)
+               dma_free_coherent(dev, size,
+                               xhci->erst_audio.entries, xhci->erst_audio.erst_dma_addr);
+       xhci->erst_audio.entries = NULL;
+       xhci_info(xhci, "%s: Freed ERST for Audio offloading", __func__);
+
+       if (xhci->event_ring_audio)
+               xhci_ring_free(xhci, xhci->event_ring_audio);
+       xhci->event_ring_audio = NULL;
+       xhci_info(xhci, "%s: Freed event ring for Audio offloading", __func__);
+#endif
+
        if (xhci->lpm_command)
                xhci_free_command(xhci, xhci->lpm_command);
        xhci->lpm_command = NULL;
@@ -2035,6 +2053,32 @@ static int xhci_check_trb_in_td_math(struct xhci_hcd *xhci)
        return 0;
 }
 
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+static void xhci_set_hc_event_deq_audio(struct xhci_hcd *xhci)
+{
+       u64 temp;
+       dma_addr_t deq;
+
+       deq = xhci_trb_virt_to_dma(xhci->event_ring_audio->deq_seg,
+                       xhci->event_ring_audio->dequeue);
+       if (deq == 0 && !in_interrupt())
+               xhci_warn(xhci, "WARN something wrong with SW event ring "
+                               "dequeue ptr.\n");
+       /* Update HC event ring dequeue pointer */
+       temp = xhci_read_64(xhci, &xhci->ir_set_audio->erst_dequeue);
+       temp &= ERST_PTR_MASK;
+       /* Don't clear the EHB bit (which is RW1C) because
+        * there might be more events to service.
+        */
+       temp &= ~ERST_EHB;
+       xhci_info(xhci,
+                       "//[%s] Write event ring dequeue pointer = 0x%llx, "
+                       "preserving EHB bit",__func__, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp);
+       xhci_write_64(xhci, ((u64) deq & (u64) ~ERST_PTR_MASK) | temp,
+                       &xhci->ir_set_audio->erst_dequeue);
+}
+#endif
+
 static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
 {
        u64 temp;
@@ -2455,7 +2499,9 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
        xhci_print_run_regs(xhci);
        /* Set ir_set to interrupt register set 0 */
        xhci->ir_set = &xhci->run_regs->ir_set[0];
-
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+       xhci->ir_set_audio = &xhci->run_regs->ir_set[1];
+#endif
        /*
         * Event ring setup: Allocate a normal ring, but also setup
         * the event ring segment table (ERST).  Section 4.9.3.
@@ -2521,6 +2567,73 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
                        "Wrote ERST address to ir_set 0.");
        xhci_print_ir_set(xhci, 0);
 
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+       xhci->save_addr = dma_alloc_coherent(dev, sizeof(PAGE_SIZE), &dma,
+                       flags);
+       xhci->save_dma = dma;
+       xhci_info(xhci, "// Save address = 0x%llx (DMA), %p (virt)",
+                       (unsigned long long)xhci->save_dma, xhci->save_addr);
+       /* for AUDIO erst */
+       xhci->event_ring_audio = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT,
+                                       0, flags);
+       if (!xhci->event_ring_audio)
+               goto fail;
+       if (xhci_check_trb_in_td_math(xhci) < 0)
+               goto fail;
+
+       xhci->erst_audio.entries = dma_alloc_coherent(dev,
+                       sizeof(struct xhci_erst_entry) * ERST_NUM_SEGS, &dma,
+                       flags);
+       if (!xhci->erst_audio.entries)
+               goto fail;
+       xhci_info(xhci,
+                       "// Allocated event ring segment table at 0x%llx",
+                       (unsigned long long)dma);
+
+       memset(xhci->erst_audio.entries, 0, sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS);
+       xhci->erst_audio.num_entries = ERST_NUM_SEGS;
+       xhci->erst_audio.erst_dma_addr = dma;
+       xhci_info(xhci,
+                       "// Set ERST to 0; private num segs = %i, virt addr = %p, dma addr = 0x%llx",
+                       xhci->erst.num_entries,
+                       xhci->erst.entries,
+                       (unsigned long long)xhci->erst.erst_dma_addr);
+
+       /* set ring base address and size for each segment table entry */
+       for (val = 0, seg = xhci->event_ring_audio->first_seg; val < ERST_NUM_SEGS; val++) {
+               struct xhci_erst_entry *entry = &xhci->erst_audio.entries[val];
+               entry->seg_addr = cpu_to_le64(seg->dma);
+               entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT);
+               entry->rsvd = 0;
+               seg = seg->next;
+       }
+
+       /* set ERST count with the number of entries in the segment table */
+       val = readl(&xhci->ir_set_audio->erst_size);
+       val &= ERST_SIZE_MASK;
+       val |= ERST_NUM_SEGS;
+       xhci_info(xhci,
+                       "// Write ERST size = %i to ir_set 0 (some bits preserved)",
+                       val);
+       writel(val, &xhci->ir_set_audio->erst_size);
+
+       xhci_info(xhci,
+                       "// Set ERST entries to point to event ring.");
+       /* set the segment table base address */
+       xhci_info(xhci,
+                       "// Set ERST base address for ir_set 0 = 0x%llx",
+                       (unsigned long long)xhci->erst_audio.erst_dma_addr);
+       val_64 = xhci_read_64(xhci, &xhci->ir_set_audio->erst_base);
+       val_64 &= ERST_PTR_MASK;
+       val_64 |= (xhci->erst_audio.erst_dma_addr & (u64) ~ERST_PTR_MASK);
+       xhci_write_64(xhci, val_64, &xhci->ir_set_audio->erst_base);
+
+       /* Set the event ring dequeue address */
+       xhci_set_hc_event_deq_audio(xhci);
+       xhci_info(xhci,
+                       "// Wrote ERST address to ir_set 1.");
+       xhci_print_ir_set(xhci, 1);
+#endif
        /*
         * XXX: Might need to set the Interrupter Moderation Register to
         * something other than the default (~1ms minimum between interrupts).
index 1cb6eaef4ae14efcee09e5adfe0e913e4e54525c..a8703f6ba527c114199c5b558bc1373a889dba73 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/usb/phy.h>
 #include <linux/slab.h>
+#include <linux/phy/phy.h>
 #include <linux/acpi.h>
 
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+#include <linux/usb/exynos_usb_audio.h>
+#endif
+
 #include "xhci.h"
 #include "xhci-plat.h"
 #include "xhci-mvebu.h"
@@ -30,6 +35,9 @@ static struct hc_driver __read_mostly xhci_plat_hc_driver;
 
 static int xhci_plat_setup(struct usb_hcd *hcd);
 static int xhci_plat_start(struct usb_hcd *hcd);
+void __iomem           *usb3_portsc;
+static u32 pp_set_delayed;
+#define PORTSC_OFFSET  0x430
 
 static const struct xhci_driver_overrides xhci_plat_overrides __initconst = {
        .extra_priv_size = sizeof(struct xhci_plat_priv),
@@ -55,16 +63,6 @@ static int xhci_priv_init_quirk(struct usb_hcd *hcd)
        return priv->init_quirk(hcd);
 }
 
-static int xhci_priv_resume_quirk(struct usb_hcd *hcd)
-{
-       struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd);
-
-       if (!priv->resume_quirk)
-               return 0;
-
-       return priv->resume_quirk(hcd);
-}
-
 static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
 {
        /*
@@ -80,12 +78,20 @@ static int xhci_plat_setup(struct usb_hcd *hcd)
 {
        int ret;
 
-
        ret = xhci_priv_init_quirk(hcd);
        if (ret)
                return ret;
 
-       return xhci_gen_setup(hcd, xhci_plat_quirks);
+       ret = xhci_gen_setup(hcd, xhci_plat_quirks);
+
+       /*
+        * DWC3 WORKAROUND: xhci reset clears PHY CR port settings,
+        * so USB3.0 PHY should be tuned again.
+        */
+       if (hcd->phy)
+               phy_tune(hcd->phy, OTG_STATE_A_HOST);
+
+       return ret;
 }
 
 static int xhci_plat_start(struct usb_hcd *hcd)
@@ -94,6 +100,73 @@ static int xhci_plat_start(struct usb_hcd *hcd)
        return xhci_run(hcd);
 }
 
+static ssize_t
+xhci_plat_show_ss_compliance(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+       u32                     reg;
+       void __iomem *reg_base;
+
+       reg_base = hcd->regs;
+       reg = readl(reg_base + PORTSC_OFFSET);
+
+       return snprintf(buf, PAGE_SIZE, "0x%x\n", reg);
+}
+
+static ssize_t
+xhci_platg_store_ss_compliance(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t n)
+{
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+       int             value;
+       u32                     reg;
+       void __iomem *reg_base;
+
+       if (sscanf(buf, "%d", &value) != 1)
+               return -EINVAL;
+
+       reg_base = hcd->regs;
+
+       if (value == 1) {
+               /* PORTSC PLS is set to 10, LWS to 1 */
+               reg = readl(reg_base + PORTSC_OFFSET);
+               reg &= ~((0xF << 5) | (1 << 16));
+               reg |= (10 << 5) | (1 << 16);
+               writel(reg, reg_base + PORTSC_OFFSET);
+               pr_info("Super speed host compliance enabled portsc 0x%x\n", reg);
+       } else
+               pr_info("Only 1 is allowed for input value\n");
+
+       return n;
+}
+
+static DEVICE_ATTR(ss_compliance, S_IWUSR | S_IRUSR | S_IRGRP,
+       xhci_plat_show_ss_compliance, xhci_platg_store_ss_compliance);
+
+static ssize_t
+xhci_plat_show_l2_state(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+       return snprintf(buf, PAGE_SIZE, "%d\n",
+                       xhci->l2_state);
+}
+
+static DEVICE_ATTR(l2_state, S_IRUSR | S_IRGRP | S_IROTH,
+       xhci_plat_show_l2_state, NULL);
+
+static struct attribute *exynos_xhci_attributes[] = {
+       &dev_attr_l2_state.attr,
+       &dev_attr_ss_compliance.attr,
+       NULL
+};
+
+static const struct attribute_group xhci_plat_attr_group = {
+       .attrs = exynos_xhci_attributes,
+};
 #ifdef CONFIG_OF
 static const struct xhci_plat_priv xhci_plat_marvell_armada = {
        .init_quirk = xhci_mvebu_mbus_init_quirk,
@@ -150,8 +223,40 @@ static const struct of_device_id usb_xhci_of_match[] = {
 MODULE_DEVICE_TABLE(of, usb_xhci_of_match);
 #endif
 
+void xhci_portsc_power_off(void __iomem *portsc, u32 on)
+{
+       u32 reg;
+
+       reg = readl(portsc);
+
+       if (on)
+               reg |= PORT_POWER;
+       else
+               reg &= ~PORT_POWER;
+
+       writel(reg, portsc);
+       reg = readl(portsc);
+
+       pr_info("%s, reg = 0x%x addr = %p\n",
+               __func__, reg, portsc);
+}
+
+int xhci_portsc_set(u32 on)
+{
+       if (usb3_portsc != NULL) {
+               xhci_portsc_power_off(usb3_portsc, 0);
+               pp_set_delayed = 0;
+               return 0;
+       }
+
+       pp_set_delayed = 1;
+       pr_info("%s, usb3_portsc is NULL\n", __func__);
+       return -EIO;
+}
+
 static int xhci_plat_probe(struct platform_device *pdev)
 {
+       struct device           *parent = pdev->dev.parent;
        const struct of_device_id *match;
        const struct hc_driver  *driver;
        struct device           *sysdev;
@@ -162,6 +267,15 @@ static int xhci_plat_probe(struct platform_device *pdev)
        int                     ret;
        int                     irq;
 
+       struct wake_lock        *wakelock;
+       int                     value;
+
+       dev_info(&pdev->dev, "XHCI PLAT START\n");
+
+       wakelock = kzalloc(sizeof(struct wake_lock), GFP_KERNEL);
+       wake_lock_init(wakelock, WAKE_LOCK_SUSPEND, dev_name(&pdev->dev));
+       wake_lock(wakelock);
+
        if (usb_disabled())
                return -ENODEV;
 
@@ -227,6 +341,25 @@ static int xhci_plat_probe(struct platform_device *pdev)
        hcd->rsrc_start = res->start;
        hcd->rsrc_len = resource_size(res);
 
+       usb3_portsc = hcd->regs + PORTSC_OFFSET;
+       pr_info("get usb3_portsc addr = %p pp_set = %d\n",
+               usb3_portsc, pp_set_delayed);
+
+       if (pp_set_delayed) {
+               xhci_portsc_power_off(usb3_portsc, 0);
+               pp_set_delayed = 0;
+       }
+
+       /* Get USB2.0 PHY for main hcd */
+       if (parent) {
+               hcd->phy = devm_phy_get(parent, "usb2-phy");
+               if (IS_ERR_OR_NULL(hcd->phy)) {
+                       hcd->phy = NULL;
+                       dev_err(&pdev->dev,
+                               "%s: failed to get phy\n", __func__);
+               }
+       }
+
        /*
         * Not all platforms have a clk so it is not an error if the
         * clock does not exists.
@@ -255,6 +388,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
        device_wakeup_enable(hcd->self.controller);
 
        xhci->clk = clk;
+       xhci->wakelock = wakelock;
        xhci->main_hcd = hcd;
        xhci->shared_hcd = __usb_create_hcd(driver, sysdev, &pdev->dev,
                        dev_name(&pdev->dev), hcd);
@@ -281,6 +415,24 @@ static int xhci_plat_probe(struct platform_device *pdev)
                        goto put_usb3_hcd;
        }
 
+       /* Get USB3.0 PHY to tune the PHY */
+       if (parent) {
+               xhci->shared_hcd->phy = devm_phy_get(parent, "usb3-phy");
+               if (IS_ERR_OR_NULL(hcd->phy)) {
+                       hcd->phy = NULL;
+                       dev_err(&pdev->dev,
+                               "%s: failed to get phy\n", __func__);
+               }
+       }
+
+       ret = of_property_read_u32(parent->of_node, "xhci_l2_support", &value);
+       if (ret == 0 && value == 1)
+               xhci->quirks |= XHCI_L2_SUPPORT;
+       else {
+               dev_err(&pdev->dev,
+                       "can't get xhci l2 support, error = %d\n", ret);
+       }
+
        ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
        if (ret)
                goto disable_usb_phy;
@@ -292,6 +444,31 @@ static int xhci_plat_probe(struct platform_device *pdev)
        if (ret)
                goto dealloc_usb2_hcd;
 
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+       ret = of_property_read_u32(parent->of_node, "usb_audio_offloading", &value);
+       if (ret == 0 && value == 1) {
+               ret = exynos_usb_audio_init(parent, pdev);
+               if (ret) {
+                       dev_err(&pdev->dev, "USB Audio INIT fail\n");
+                       return ret;
+               } else {
+                       dev_info(&pdev->dev, "USB Audio offloading is supported\n");
+               }
+       } else {
+               dev_err(&pdev->dev, "can't get audio support, error = %d\n", ret);
+               return ret;
+       }
+
+       xhci->out_dma = xhci_data.out_data_dma;
+       xhci->out_addr = xhci_data.out_data_addr;
+       xhci->in_dma = xhci_data.in_data_dma;
+       xhci->in_addr = xhci_data.in_data_addr;
+#endif
+
+       ret = sysfs_create_group(&pdev->dev.kobj, &xhci_plat_attr_group);
+       if (ret)
+               dev_err(&pdev->dev, "failed to create xhci-plat attributes\n");
+
        device_enable_async_suspend(&pdev->dev);
        pm_runtime_put_noidle(&pdev->dev);
 
@@ -329,15 +506,46 @@ disable_runtime:
 
 static int xhci_plat_remove(struct platform_device *dev)
 {
+       struct device   *parent = dev->dev.parent;
        struct usb_hcd  *hcd = platform_get_drvdata(dev);
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
        struct clk *clk = xhci->clk;
+       int timeout = 0;
+
+       dev_info(&dev->dev, "XHCI PLAT REMOVE\n");
+
+       usb3_portsc = NULL;
+       pp_set_delayed = 0;
+
+       /*
+        * Sometimes deadlock occurred in this function.
+        * So, below waiting for completion of hub_event was added.
+        */
+       while (xhci->shared_hcd->is_in_hub_event || hcd->is_in_hub_event) {
+               msleep(10);
+               timeout += 10;
+               if (timeout >= XHCI_HUB_EVENT_TIMEOUT) {
+                       xhci_err(xhci,
+                               "ERROR: hub_event completion timeout\n");
+                       break;
+               }
+       }
+       xhci_dbg(xhci, "%s: waited %dmsec", __func__, timeout);
 
        xhci->xhc_state |= XHCI_STATE_REMOVING;
 
        usb_remove_hcd(xhci->shared_hcd);
        usb_phy_shutdown(hcd->usb_phy);
 
+       /*
+        * In usb_remove_hcd, phy_exit is called if phy is not NULL.
+        * However, in the case that PHY was turn on or off as runtime PM,
+        * PHY sould not exit at this time. So, to prevent the PHY exit,
+        * PHY pointer have to be NULL.
+        */
+       if (parent && hcd->phy)
+               hcd->phy = NULL;
+
        usb_remove_hcd(hcd);
        usb_put_hcd(xhci->shared_hcd);
 
@@ -345,6 +553,10 @@ static int xhci_plat_remove(struct platform_device *dev)
                clk_disable_unprepare(clk);
        usb_put_hcd(hcd);
 
+       dev_info(&dev->dev, "WAKE UNLOCK\n");
+       wake_unlock(xhci->wakelock);
+       wake_lock_destroy(xhci->wakelock);
+
        pm_runtime_set_suspended(&dev->dev);
        pm_runtime_disable(&dev->dev);
 
@@ -353,9 +565,13 @@ static int xhci_plat_remove(struct platform_device *dev)
 
 static int __maybe_unused xhci_plat_suspend(struct device *dev)
 {
-       struct usb_hcd  *hcd = dev_get_drvdata(dev);
-       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-       int ret;
+       /*
+        *struct usb_hcd        *hcd = dev_get_drvdata(dev);
+        *struct xhci_hcd       *xhci = hcd_to_xhci(hcd);
+        *int ret;
+        */
+
+       pr_info("[%s] \n",__func__);
 
        /*
         * xhci_suspend() needs `do_wakeup` to know whether host is allowed
@@ -365,28 +581,37 @@ static int __maybe_unused xhci_plat_suspend(struct device *dev)
         * reconsider this when xhci_plat_suspend enlarges its scope, e.g.,
         * also applies to runtime suspend.
         */
-       ret = xhci_suspend(xhci, device_may_wakeup(dev));
 
-       if (!device_may_wakeup(dev) && !IS_ERR(xhci->clk))
-               clk_disable_unprepare(xhci->clk);
-
-       return ret;
+       /*
+        *ret = xhci_suspend(xhci, device_may_wakeup(dev));
+        *
+        *if (!device_may_wakeup(dev) && !IS_ERR(xhci->clk))
+        *      clk_disable_unprepare(xhci->clk);
+        */
+       return 0;
 }
 
 static int __maybe_unused xhci_plat_resume(struct device *dev)
 {
-       struct usb_hcd  *hcd = dev_get_drvdata(dev);
-       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-       int ret;
-
-       if (!device_may_wakeup(dev) && !IS_ERR(xhci->clk))
-               clk_prepare_enable(xhci->clk);
+       /*
+        *struct usb_hcd        *hcd = dev_get_drvdata(dev);
+        *struct xhci_hcd       *xhci = hcd_to_xhci(hcd);
+        *int ret;
+        */
 
-       ret = xhci_priv_resume_quirk(hcd);
-       if (ret)
-               return ret;
+       pr_info("[%s] \n",__func__);
 
-       return xhci_resume(xhci, 0);
+       /*
+        *if (!device_may_wakeup(dev) && !IS_ERR(xhci->clk))
+        *      clk_prepare_enable(xhci->clk);
+        *
+        *ret = xhci_priv_resume_quirk(hcd);
+        *if (ret)
+        *      return ret;
+        *
+        *return xhci_resume(xhci, 0);
+        */
+        return 0;
 }
 
 static int __maybe_unused xhci_plat_runtime_suspend(struct device *dev)
index 6996235e34a95357298909c5e2468674263c7754..aa761b83e8b7267552603775eb7ab654fe42df4f 100644 (file)
@@ -1676,13 +1676,15 @@ static void handle_port_status(struct xhci_hcd *xhci,
                        goto cleanup;
                } else if (!test_bit(faked_port_index,
                                     &bus_state->resuming_ports)) {
-                       xhci_dbg(xhci, "resume HS port %d\n", port_id);
+                       xhci_info(xhci, "resume HS port %d\n", port_id);
                        bus_state->resume_done[faked_port_index] = jiffies +
                                msecs_to_jiffies(USB_RESUME_TIMEOUT);
                        set_bit(faked_port_index, &bus_state->resuming_ports);
                        mod_timer(&hcd->rh_timer,
                                  bus_state->resume_done[faked_port_index]);
                        /* Do the rest in GetPortStatus */
+                       /* REWA Disable */
+                       phy_vendor_set(xhci->main_hcd->phy, 0, 0);
                }
        }
 
index bcf315b32bff0e2afa90aae55101036efb8b9209..5f31a70fa82b94975ddbfe6f9e51e401abc1d3fc 100644 (file)
 #include <linux/dmi.h>
 #include <linux/dma-mapping.h>
 
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+#include <linux/usb/exynos_usb_audio.h>
+#endif
+
 #include "xhci.h"
 #include "xhci-trace.h"
 #include "xhci-mtk.h"
@@ -593,6 +597,11 @@ int xhci_run(struct usb_hcd *hcd)
        xhci_dbg_trace(xhci, trace_xhci_dbg_init,
                        "ERST deq = 64'h%0lx", (long unsigned int) temp_64);
 
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+       temp_64 = xhci_read_64(xhci, &xhci->ir_set_audio->erst_dequeue);
+       temp_64 &= ~ERST_PTR_MASK;
+       xhci_info(xhci, "ERST2 deq = 64'h%0lx", (long unsigned int) temp_64);
+#endif
        xhci_dbg_trace(xhci, trace_xhci_dbg_init,
                        "// Set the interrupt modulation register");
        temp = readl(&xhci->ir_set->irq_control);
@@ -604,6 +613,17 @@ int xhci_run(struct usb_hcd *hcd)
        temp |= (u32) ((xhci->quirks & XHCI_MTK_HOST) ? 20 : 160);
        writel(temp, &xhci->ir_set->irq_control);
 
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+       xhci_info(xhci, "// [USB Audio] Set the interrupt modulation register");
+       temp = readl(&xhci->ir_set_audio->irq_control);
+       temp &= ~ER_IRQ_INTERVAL_MASK;
+       /*
+        * the increment interval is 8 times as much as that defined
+        * in xHCI spec on MTK's controller
+        */
+       temp |= (u32) ((xhci->quirks & XHCI_MTK_HOST) ? 20 : 160);
+       writel(temp, &xhci->ir_set_audio->irq_control);
+#endif
        /* Set the HCD state before we enable the irqs */
        temp = readl(&xhci->op_regs->command);
        temp |= (CMD_EIE);
@@ -618,6 +638,13 @@ int xhci_run(struct usb_hcd *hcd)
        writel(ER_IRQ_ENABLE(temp), &xhci->ir_set->irq_pending);
        xhci_print_ir_set(xhci, 0);
 
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+       temp = readl(&xhci->ir_set_audio->irq_pending);
+       xhci_info(xhci, "// [USB Audio] Enabling event ring interrupter %p by writing 0x%x to irq_pending",
+                       xhci->ir_set_audio, (unsigned int) ER_IRQ_ENABLE(temp));
+       writel(ER_IRQ_ENABLE(temp), &xhci->ir_set_audio->irq_pending);
+       xhci_print_ir_set(xhci, 1);
+#endif
        if (xhci->quirks & XHCI_NEC_HOST) {
                struct xhci_command *command;
 
@@ -2727,6 +2754,8 @@ static int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
                /* Callee should call reset_bandwidth() */
                goto command_cleanup;
 
+       xhci_set_deq(xhci, virt_dev->out_ctx, LAST_CTX_TO_EP_NUM(le32_to_cpu(slot_ctx->dev_info)), udev);
+
        /* Free any rings that were dropped, but not changed. */
        for (i = 1; i < 31; i++) {
                if ((le32_to_cpu(ctrl_ctx->drop_flags) & (1 << (i + 1))) &&
@@ -3539,7 +3568,7 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
         * device.
         */
        if (ret <= 0 && ret != -ENODEV)
-               return;
+               goto out;
 
        virt_dev = xhci->devs[udev->slot_id];
        slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx);
@@ -3556,6 +3585,12 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
         * Event command completion handler will free any data structures
         * associated with the slot.  XXX Can free sleep?
         */
+
+out:
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+       exynos_usb_audio_conn(0);
+#endif
+       return;
 }
 
 int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id)
@@ -3613,6 +3648,57 @@ static int xhci_reserve_host_control_ep_resources(struct xhci_hcd *xhci)
        return 0;
 }
 
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+int xhci_store_hw_info(struct usb_hcd *hcd, struct usb_device *udev)
+{
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+       struct hcd_hw_info *hwinfo = &udev->hwinfo;
+       struct xhci_virt_device *virt_dev;
+       struct xhci_erst_entry *entry = &xhci->erst_audio.entries[0];
+
+       virt_dev = xhci->devs[udev->slot_id];
+
+       hwinfo->dcbaa_dma = xhci->dcbaa->dma;
+       hwinfo->save_dma = xhci->save_dma;
+       hwinfo->cmd_ring = xhci->op_regs->cmd_ring;
+       hwinfo->slot_id = xhci->slot_id;
+       hwinfo->in_dma = xhci->in_dma;
+       hwinfo->in_buf = xhci->in_addr;
+       hwinfo->in_ctx = virt_dev->in_ctx->dma;
+       hwinfo->out_ctx = virt_dev->out_ctx->dma;
+       hwinfo->erst_addr = entry->seg_addr;
+       hwinfo->speed = udev->speed;
+
+       return 0;
+}
+#endif
+
+int xhci_set_deq(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
+               unsigned int last_ep, struct usb_device *udev)
+{
+       int i;
+       int last_ep_ctx = 31;
+
+       if (last_ep < 31)
+               last_ep_ctx = last_ep + 1;
+       for (i = 0; i < last_ep_ctx; ++i) {
+               unsigned int epaddr = xhci_get_endpoint_address(i);
+               struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, ctx, i);
+               if (epaddr == udev->hwinfo.in_ep ) {
+                       pr_debug("[%s] set in deq : %#08llx \n",__func__, ep_ctx->deq);
+                       if (udev->hwinfo.in_deq)
+                               udev->hwinfo.old_in_deq = udev->hwinfo.in_deq;
+                       udev->hwinfo.in_deq = ep_ctx->deq;
+               } else if (epaddr == udev->hwinfo.out_ep) {
+                       pr_debug("[%s] set out deq : %#08llx \n",__func__, ep_ctx->deq);
+                       if (udev->hwinfo.out_deq)
+                               udev->hwinfo.old_out_deq = udev->hwinfo.out_deq;
+                       udev->hwinfo.out_deq = ep_ctx->deq;
+               }
+       }
+
+       return 0;
+}
 
 /*
  * Returns 0 if the xHC ran out of device slots, the Enable Slot command
@@ -3627,6 +3713,9 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
        int ret, slot_id;
        struct xhci_command *command;
 
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+       exynos_usb_audio_conn(1);
+#endif
        command = xhci_alloc_command(xhci, false, true, GFP_KERNEL);
        if (!command)
                return 0;
@@ -3872,6 +3961,9 @@ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev,
        xhci_dbg_trace(xhci, trace_xhci_dbg_address,
                       "Internal device address = %d",
                       le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK);
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+       xhci_store_hw_info(hcd, udev);
+#endif
 out:
        mutex_unlock(&xhci->mutex);
        if (command) {
@@ -4885,6 +4977,21 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
 }
 EXPORT_SYMBOL_GPL(xhci_gen_setup);
 
+int xhci_wake_lock(struct usb_hcd *hcd, int is_lock) {
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+       xhci_info(xhci, "%s: WAKE %s\n", __func__, is_lock ? "LOCK" : "UNLOCK");
+       if (is_lock) {
+               xhci->l2_state = 0;
+               wake_lock(xhci->wakelock);
+       } else {
+               xhci->l2_state = 1;
+               wake_unlock(xhci->wakelock);
+       }
+
+       return 0;
+}
+
 static const struct hc_driver xhci_hc_driver = {
        .description =          "xhci-hcd",
        .product_desc =         "xHCI Host Controller",
@@ -4896,6 +5003,7 @@ static const struct hc_driver xhci_hc_driver = {
        .irq =                  xhci_irq,
        .flags =                HCD_MEMORY | HCD_USB3 | HCD_SHARED,
 
+       .wake_lock =            xhci_wake_lock,
        /*
         * basic lifecycle operations
         */
@@ -4933,6 +5041,8 @@ static const struct hc_driver xhci_hc_driver = {
         */
        .hub_control =          xhci_hub_control,
        .hub_status_data =      xhci_hub_status_data,
+       .hub_check_speed = xhci_hub_check_speed,
+       .usb_l2_check = xhci_check_usbl2_support,
        .bus_suspend =          xhci_bus_suspend,
        .bus_resume =           xhci_bus_resume,
 
index f5fb1f4a092cb6f1e41b740a47bf8ec453da2f6e..b952e81877bd8b1c11ad5f570a76acf70032d830 100644 (file)
@@ -29,6 +29,8 @@
 #include <linux/kernel.h>
 #include <linux/usb/hcd.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/wakelock.h>
+#include <linux/phy/phy.h>
 
 /* Code sharing between pci-quirks and xhci hcd */
 #include       "xhci-ext-caps.h"
@@ -1698,6 +1700,13 @@ struct xhci_hub {
        u8      psi_uid_count;
 };
 
+/*
+ * Sometimes deadlock occurred between hub_event and remove_hcd.
+ * In order to prevent it, waiting for completion of hub_event was added.
+ * This is a timeout (300msec) value for the waiting.
+ */
+#define XHCI_HUB_EVENT_TIMEOUT (300)
+
 /* There is one xhci_hcd structure per controller */
 struct xhci_hcd {
        struct usb_hcd *main_hcd;
@@ -1709,6 +1718,9 @@ struct xhci_hcd {
        struct xhci_doorbell_array __iomem *dba;
        /* Our HCD's current interrupter register set */
        struct  xhci_intr_reg __iomem *ir_set;
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+       struct  xhci_intr_reg __iomem *ir_set_audio;
+#endif
 
        /* Cached register copies of read-only HC data */
        __u32           hcs_params1;
@@ -1749,6 +1761,18 @@ struct xhci_hcd {
        struct xhci_command     *current_cmd;
        struct xhci_ring        *event_ring;
        struct xhci_erst        erst;
+
+#ifdef CONFIG_SND_EXYNOS_USB_AUDIO
+       struct xhci_ring        *event_ring_audio;
+       struct xhci_erst        erst_audio;
+       dma_addr_t save_dma;
+       void *save_addr;
+       dma_addr_t out_dma;
+       void *out_addr;
+       dma_addr_t in_dma;
+       void *in_addr;
+#endif
+
        /* Scratchpad */
        struct xhci_scratchpad  *scratchpad;
        /* Store LPM test failed devices' information */
@@ -1770,6 +1794,9 @@ struct xhci_hcd {
        struct dma_pool *small_streams_pool;
        struct dma_pool *medium_streams_pool;
 
+       struct wake_lock *wakelock;
+       int                     l2_state;
+
        /* Host controller watchdog timer structures */
        unsigned int            xhc_state;
 
@@ -1831,6 +1858,7 @@ struct xhci_hcd {
 #define XHCI_LIMIT_ENDPOINT_INTERVAL_7 (1 << 26)
 /* Reserved. It was XHCI_U2_DISABLE_WAKE */
 #define XHCI_ASMEDIA_MODIFY_FLOWCONTROL        (1 << 28)
+#define XHCI_L2_SUPPORT        (1 << 29)
 #define XHCI_SUSPEND_DELAY     (1 << 30)
 
        unsigned int            num_active_eps;
@@ -2022,6 +2050,9 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated);
 irqreturn_t xhci_irq(struct usb_hcd *hcd);
 irqreturn_t xhci_msi_irq(int irq, void *hcd);
 int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev);
+int xhci_store_hw_info(struct usb_hcd *hcd, struct usb_device *udev);
+int xhci_set_deq(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx,
+               unsigned int last_ep, struct usb_device *udev);
 int xhci_alloc_tt_info(struct xhci_hcd *xhci,
                struct xhci_virt_device *virt_dev,
                struct usb_device *hdev,
@@ -2086,6 +2117,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
 int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
 int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);
 void xhci_hc_died(struct xhci_hcd *xhci);
+int xhci_hub_check_speed(struct usb_hcd *hcd);
+int xhci_check_usbl2_support(struct usb_hcd *hcd);
 
 #ifdef CONFIG_PM
 int xhci_bus_suspend(struct usb_hcd *hcd);
index 4192a1755ccbaf18e31836db654804f039b61f25..785a28cdf00823a40777352ff5fc8de3beb92698 100644 (file)
@@ -544,6 +544,28 @@ struct usb3_lpm_parameters {
        int timeout;
 };
 
+struct hcd_hw_info {
+       /* for XHCI */
+       int slot_id;
+       dma_addr_t erst_addr;
+       dma_addr_t dcbaa_dma;
+       dma_addr_t in_ctx;
+       dma_addr_t out_ctx;
+       dma_addr_t save_dma;
+       u64 cmd_ring;
+       u64 old_out_deq;
+       u64 old_in_deq;
+       u64 out_deq;
+       u64 in_deq;
+       int in_ep;
+       int out_ep;
+       int speed;
+       void *out_buf;
+       u64 out_dma;
+       void *in_buf;
+       u64 in_dma;
+};
+
 /**
  * struct usb_device - kernel's representation of a USB device
  * @devnum: device number; address on a USB bus
@@ -641,6 +663,9 @@ struct usb_device {
        struct usb_host_endpoint *ep_out[16];
 
        char **rawdescriptors;
+       int rawdesc_length;
+
+       struct hcd_hw_info hwinfo;
 
        unsigned short bus_mA;
        u8 portnum;
index 09da0c80497d7823b5b62e40c489b358565b5f60..f9e77cd252467bbfdc6a0143078c175e3f5eacda 100644 (file)
@@ -211,6 +211,12 @@ struct usb_function {
        void                    (*free_func)(struct usb_function *f);
        struct module           *mod;
 
+#ifdef CONFIG_USB_CONFIGFS_UEVENT
+       /* Optional function for vendor specific processing */
+       int                     (*ctrlrequest)(struct usb_function *,
+                                       const struct usb_ctrlrequest *);
+#endif
+
        /* runtime state management */
        int                     (*set_alt)(struct usb_function *,
                                        unsigned interface, unsigned alt);
diff --git a/include/linux/usb/exynos_usb_audio.h b/include/linux/usb/exynos_usb_audio.h
new file mode 100644 (file)
index 0000000..aa78d8e
--- /dev/null
@@ -0,0 +1,68 @@
+#include <sound/samsung/abox.h>
+
+/* for KM */
+
+#define USB_AUDIO_MEM_BASE     0xC0000000
+
+#define USB_AUDIO_SAVE_RESTORE USB_AUDIO_MEM_BASE
+#define USB_AUDIO_DEV_CTX      USB_AUDIO_SAVE_RESTORE+PAGE_SIZE
+#define USB_AUDIO_INPUT_CTX    USB_AUDIO_DEV_CTX+PAGE_SIZE
+#define USB_AUDIO_OUT_DEQ      USB_AUDIO_INPUT_CTX+PAGE_SIZE
+#define USB_AUDIO_IN_DEQ       USB_AUDIO_OUT_DEQ+PAGE_SIZE
+#define USB_AUDIO_ERST         USB_AUDIO_IN_DEQ+PAGE_SIZE
+#define USB_AUDIO_DESC         USB_AUDIO_ERST+PAGE_SIZE
+#define USB_AUDIO_PCM_OUTBUF   USB_AUDIO_MEM_BASE+0x100000
+#define USB_AUDIO_PCM_INBUF    USB_AUDIO_MEM_BASE+0x800000
+
+#define USB_AUDIO_XHCI_BASE    0x10c00000
+
+struct host_data {
+       dma_addr_t out_data_dma;
+       dma_addr_t in_data_dma;
+       void *out_data_addr;
+       void *in_data_addr;
+};
+
+extern struct host_data xhci_data;
+
+struct exynos_usb_audio {
+       struct usb_device *udev;
+       struct platform_device *abox;
+       struct platform_device *hcd_pdev;
+       struct mutex    lock;
+       struct completion in_task_done;
+       struct completion out_task_done;
+
+       u64 out_buf_addr;
+       u64 in_buf_addr;
+       u64 pcm_offset;
+       u64 desc_addr;
+       u64 offset;
+
+       /* for hw_info */
+       u64 dcbaa_dma;
+       u64 in_ctx;
+       u64 out_ctx;
+       u64 erst_addr;
+
+       int speed;
+       /* 1: in, 0: out */
+       int set_ep;
+       int is_audio;
+
+       void *pcm_buf;
+       u64 save_dma;
+};
+
+int exynos_usb_audio_map_buf(struct usb_device *udev);
+int exynos_usb_audio_pcmbuf(struct usb_device *udev);
+int exynos_usb_audio_setrate(int intf, int rate, int alt);
+int exynos_usb_audio_setintf(struct usb_device *udev, int iface, int alt, int direction);
+int exynos_usb_audio_conn(int is_conn);
+int exynos_usb_audio_pcm(int is_open, int direction);
+int exynos_usb_audio_l2(int is_l2);
+int exynos_usb_audio_desc(struct usb_device *udev);
+int exynos_usb_audio_hcd(struct usb_device *udev);
+int exynos_usb_audio_init(struct device *dev, struct platform_device *pdev);
+int exynos_usb_audio_exit(void);
+void exynos_usb_audio_set_device(struct usb_device *udev);
index a1f03ebfde47f3b64309adad81d05a4c80fdafae..399d2786d7d86b552769af90b70f01384cdcd4b9 100644 (file)
@@ -187,6 +187,7 @@ struct usb_hcd {
        struct usb_hcd          *shared_hcd;
        struct usb_hcd          *primary_hcd;
 
+       bool                    is_in_hub_event;
 
 #define HCD_BUFFER_POOLS       4
        struct dma_pool         *pool[HCD_BUFFER_POOLS];
@@ -257,6 +258,7 @@ struct hc_driver {
 #define        HCD_MASK        0x0070
 #define        HCD_BH          0x0100          /* URB complete in BH context */
 
+       int     (*wake_lock) (struct usb_hcd *hcd, int is_lock);
        /* called to init HCD and root hub */
        int     (*reset) (struct usb_hcd *hcd);
        int     (*start) (struct usb_hcd *hcd);
@@ -312,6 +314,8 @@ struct hc_driver {
        int     (*hub_control) (struct usb_hcd *hcd,
                                u16 typeReq, u16 wValue, u16 wIndex,
                                char *buf, u16 wLength);
+       int     (*hub_check_speed) (struct usb_hcd *hcd);
+       int     (*usb_l2_check) (struct usb_hcd *hcd);
        int     (*bus_suspend)(struct usb_hcd *);
        int     (*bus_resume)(struct usb_hcd *);
        int     (*start_port_reset)(struct usb_hcd *, unsigned port_num);
index 4031f47629ecf264113b3b3d402c6a78ce2a955a..e3a88d52ed54eae7992fa444468d2b74b905b06b 100644 (file)
@@ -19,6 +19,8 @@ int of_usb_update_otg_caps(struct device_node *np,
 struct device_node *usb_of_get_child_node(struct device_node *parent,
                        int portnum);
 struct device *usb_of_get_companion_dev(struct device *dev);
+
+unsigned int of_usb_get_suspend_clk_freq(struct device *dev);
 #else
 static inline enum usb_dr_mode
 of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0)
diff --git a/include/linux/usb/samsung_usb.h b/include/linux/usb/samsung_usb.h
new file mode 100644 (file)
index 0000000..cea8a67
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ *             http://www.samsung.com/
+ *
+ * Common defines for samsung usb controllers
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __LINUX_USB_SAMSUNG_USB_H
+#define __LINUX_USB_SAMSUNG_USB_H
+
+enum samsung_cpu_type {
+       TYPE_S3C64XX,
+       TYPE_EXYNOS4210,
+       TYPE_EXYNOS5250,
+       TYPE_EXYNOS5430,
+       TYPE_EXYNOS7420,
+       TYPE_EXYNOS7580,
+       TYPE_EXYNOS8890,
+       TYPE_EXYNOS8895,
+};
+
+enum samsung_usb_ip_type {
+       TYPE_USB3DRD = 0,
+       TYPE_USB3HOST,
+       TYPE_USB2DRD,
+       TYPE_USB2HOST,
+};
+
+enum samsung_phy_set_option {
+       SET_DPPULLUP_ENABLE,
+       SET_DPPULLUP_DISABLE,
+       SET_DPDM_PULLDOWN,
+};
+#endif /* __LINUX_USB_SAMSUNG_USB_H */