From f09582feba4416547ae8de44fa77feaa12ad8fc1 Mon Sep 17 00:00:00 2001 From: Kisang Lee Date: Thu, 17 May 2018 20:59:31 +0900 Subject: [PATCH] [COMMON] usb: add USB driver for Exynos Change-Id: Ifa547a52a4b88afd8f35c0af064e71c2706fb5f3 Signed-off-by: Kisang Lee --- drivers/usb/common/common.c | 20 + drivers/usb/core/config.c | 19 +- drivers/usb/core/hcd.c | 16 +- drivers/usb/core/hub.c | 13 +- drivers/usb/dwc3/Kconfig | 1 - drivers/usb/dwc3/Makefile | 2 + drivers/usb/dwc3/core.c | 531 +++++++++++++++++------ drivers/usb/dwc3/core.h | 107 ++++- drivers/usb/dwc3/drd.c | 14 +- drivers/usb/dwc3/dwc3-exynos.c | 510 ++++++++++++++++++---- drivers/usb/dwc3/ep0.c | 40 +- drivers/usb/dwc3/gadget.c | 576 ++++++++++++++++++++++--- drivers/usb/dwc3/gadget.h | 6 + drivers/usb/dwc3/host.c | 36 +- drivers/usb/gadget/Kconfig | 34 ++ drivers/usb/gadget/configfs.c | 274 +++++++++++- drivers/usb/gadget/function/Makefile | 10 + drivers/usb/gadget/function/f_fs.c | 34 +- drivers/usb/gadget/function/f_rndis.c | 27 +- drivers/usb/gadget/function/u_serial.c | 7 +- drivers/usb/host/ehci-hub.c | 27 ++ drivers/usb/host/ehci.h | 3 + drivers/usb/host/xhci-hub.c | 72 +++- drivers/usb/host/xhci-mem.c | 115 ++++- drivers/usb/host/xhci-plat.c | 285 ++++++++++-- drivers/usb/host/xhci-ring.c | 4 +- drivers/usb/host/xhci.c | 112 ++++- drivers/usb/host/xhci.h | 33 ++ include/linux/usb.h | 25 ++ include/linux/usb/composite.h | 6 + include/linux/usb/exynos_usb_audio.h | 68 +++ include/linux/usb/hcd.h | 4 + include/linux/usb/of.h | 2 + include/linux/usb/samsung_usb.h | 39 ++ 34 files changed, 2732 insertions(+), 340 deletions(-) create mode 100644 include/linux/usb/exynos_usb_audio.h create mode 100644 include/linux/usb/samsung_usb.h diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c index 552ff7ac5a6b..fecfb673ca26 100644 --- a/drivers/usb/common/common.c +++ b/drivers/usb/common/common.c @@ -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 diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 9e3355b97396..528a4516c88c 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -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); diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 75ad6718858c..20d169150c8d 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -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 diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 8f7d94239ee3..b6e365b32d60 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -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 */ diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index ab8c0e0d3b60..4c9e56d8776a 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -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. diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 7ac725038f8d..d508dab4d14d 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -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 diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 3219d8157f5b..1b033c7e2acd 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -42,6 +42,7 @@ #include #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 diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index fc28819253f7..7d6668f10491 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -162,10 +163,35 @@ /* 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 @@ -183,6 +209,7 @@ /* 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) @@ -205,6 +232,11 @@ #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) @@ -237,6 +269,7 @@ #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) @@ -301,6 +334,12 @@ #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 @@ -315,8 +354,9 @@ #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) @@ -375,6 +415,7 @@ #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) @@ -408,8 +449,10 @@ #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 @@ -470,6 +513,19 @@ #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) diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c index 2765c51c7ef5..f5c8d1f7f597 100644 --- a/drivers/usb/dwc3/drd.c +++ b/drivers/usb/dwc3/drd.c @@ -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); } diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index e089df72f766..353e1b588b35 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -20,25 +20,323 @@ #include #include #include +#include #include #include #include +#include +#include #include #include #include +#include + +#include +#include + +#ifdef CONFIG_CPU_IDLE +#include +#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) diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 89fe53c846ef..f1caa03926f6 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -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; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 0ebdb313bb00..a9ef1ad3f05b 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -29,9 +29,12 @@ #include #include +#include +#include #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(¶ms, 0, sizeof(params)); ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); - 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) { diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index 4a3227543255..415c754b934e 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -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 */ diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 76f0b0df37c1..903d0962d1b2 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -16,9 +16,16 @@ */ #include +#ifdef CONFIG_SND_EXYNOS_USB_AUDIO +#include +#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); } diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index f572b645d21b..e07ee956bfb1 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -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 diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index bf2d0ce80c99..8c8423885827 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -8,6 +8,7 @@ #include "configfs.h" #include "u_f.h" #include "u_os_desc.h" +#include #ifdef CONFIG_USB_CONFIGFS_UEVENT #include @@ -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; diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile index d7d5673d8343..c9af6c1de939 100644 --- a/drivers/usb/gadget/function/Makefile +++ b/drivers/usb/gadget/function/Makefile @@ -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 diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 0904cb6ce4de..0366af6a7546 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -34,6 +34,7 @@ #include #include #include +#include #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; diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index c7c5b3ce1d98..4723ba2ece9b 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -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) diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 4176216d54be..33373d2e70db 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -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; diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 37ef2ac9cdae..460ea13d502d 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -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; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index a8e36170d8b8..20bcb3bf27c9 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -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 */ diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 9762333d8d7f..191dda592475 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -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; } diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index ccdc971283d0..1df84e2b16c6 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -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). diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 1cb6eaef4ae1..a8703f6ba527 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -19,8 +19,13 @@ #include #include #include +#include #include +#ifdef CONFIG_SND_EXYNOS_USB_AUDIO +#include +#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) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 6996235e34a9..aa761b83e8b7 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -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); } } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index bcf315b32bff..5f31a70fa82b 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -29,6 +29,10 @@ #include #include +#ifdef CONFIG_SND_EXYNOS_USB_AUDIO +#include +#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, diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index f5fb1f4a092c..b952e81877bd 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -29,6 +29,8 @@ #include #include #include +#include +#include /* 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); diff --git a/include/linux/usb.h b/include/linux/usb.h index 4192a1755ccb..785a28cdf008 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -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; diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 09da0c80497d..f9e77cd25246 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -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 index 000000000000..aa78d8e7ae09 --- /dev/null +++ b/include/linux/usb/exynos_usb_audio.h @@ -0,0 +1,68 @@ +#include + +/* 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); diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index a1f03ebfde47..399d2786d7d8 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -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); diff --git a/include/linux/usb/of.h b/include/linux/usb/of.h index 4031f47629ec..e3a88d52ed54 100644 --- a/include/linux/usb/of.h +++ b/include/linux/usb/of.h @@ -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 index 000000000000..cea8a67c5075 --- /dev/null +++ b/include/linux/usb/samsung_usb.h @@ -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 */ -- 2.20.1