X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;ds=sidebyside;f=drivers%2Fusb%2Fdwc3%2Fcore.c;fp=drivers%2Fusb%2Fdwc3%2Fcore.c;h=a6716e185491e3dabf7e63c2633d01a5fa6197b6;hb=8f54298dd8fabb33560a2ec7b8d101d03992ff07;hp=591bc3f7be763a21c75193e8c44e7a928ded30c2;hpb=76e5c6fd6d163f1aa63969cc982e79be1fee87a7;p=sagit-ice-cold%2Fkernel_xiaomi_msm8998.git diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 591bc3f7be76..a6716e185491 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,20 @@ /* -------------------------------------------------------------------------- */ +void dwc3_usb3_phy_suspend(struct dwc3 *dwc, int suspend) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + + if (suspend) + reg |= DWC3_GUSB3PIPECTL_SUSPHY; + else + reg &= ~DWC3_GUSB3PIPECTL_SUSPHY; + + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); +} + void dwc3_set_mode(struct dwc3 *dwc, u32 mode) { u32 reg; @@ -57,35 +72,74 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode) reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg &= ~(DWC3_GCTL_PRTCAPDIR(DWC3_GCTL_PRTCAP_OTG)); reg |= DWC3_GCTL_PRTCAPDIR(mode); + /* + * Set this bit so that device attempts three more times at SS, even + * if it failed previously to operate in SS mode. + */ + reg |= DWC3_GCTL_U2RSTECN; + reg &= ~(DWC3_GCTL_SOFITPSYNC); + reg &= ~(DWC3_GCTL_PWRDNSCALEMASK); + reg |= DWC3_GCTL_PWRDNSCALE(2); + reg |= DWC3_GCTL_U2EXIT_LFPS; dwc3_writel(dwc->regs, DWC3_GCTL, reg); + + if (mode == DWC3_GCTL_PRTCAP_OTG || mode == DWC3_GCTL_PRTCAP_HOST) { + /* + * Allow ITP generated off of ref clk based counter instead + * of UTMI/ULPI clk based counter, when superspeed only is + * active so that UTMI/ULPI PHY can be suspened. + * + * Starting with revision 2.50A, GFLADJ_REFCLK_LPM_SEL is used + * instead. + */ + if (dwc->revision < DWC3_REVISION_250A) { + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + reg |= DWC3_GCTL_SOFITPSYNC; + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + } else { + reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); + reg |= DWC3_GFLADJ_REFCLK_LPM_SEL; + dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); + } + } } /** - * dwc3_core_soft_reset - Issues core soft reset and PHY reset + * Peforms initialization of HS and SS PHYs. + * If used as a part of POR or init sequence it is recommended + * that we should perform hard reset of the PHYs prior to invoking + * this function. * @dwc: pointer to our context structure - */ -static int dwc3_core_soft_reset(struct dwc3 *dwc) +*/ +static int dwc3_init_usb_phys(struct dwc3 *dwc) { - u32 reg; int ret; - /* Before Resetting PHY, put Core in Reset */ - reg = dwc3_readl(dwc->regs, DWC3_GCTL); - reg |= DWC3_GCTL_CORESOFTRESET; - dwc3_writel(dwc->regs, DWC3_GCTL, reg); + /* Bring up PHYs */ + ret = usb_phy_init(dwc->usb2_phy); + if (ret) { + pr_err("%s: usb_phy_init(dwc->usb2_phy) returned %d\n", + __func__, ret); + return ret; + } - /* Assert USB3 PHY reset */ - reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); - reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST; - dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + if (dwc->maximum_speed == USB_SPEED_HIGH) + goto generic_phy_init; - /* Assert USB2 PHY reset */ - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + ret = usb_phy_init(dwc->usb3_phy); + if (ret == -EBUSY) { + /* + * Setting Max speed as high when USB3 PHY initialiation + * is failing and USB superspeed can't be supported. + */ + dwc->maximum_speed = USB_SPEED_HIGH; + } else if (ret) { + pr_err("%s: usb_phy_init(dwc->usb3_phy) returned %d\n", + __func__, ret); + return ret; + } - usb_phy_init(dwc->usb2_phy); - usb_phy_init(dwc->usb3_phy); +generic_phy_init: ret = phy_init(dwc->usb2_generic_phy); if (ret < 0) return ret; @@ -95,24 +149,45 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) phy_exit(dwc->usb2_generic_phy); return ret; } - mdelay(100); - /* Clear USB3 PHY reset */ + return 0; +} + +/** + * dwc3_core_reset - Issues core soft reset and PHY reset + * @dwc: pointer to our context structure + */ +static int dwc3_core_reset(struct dwc3 *dwc) +{ + int ret; + u32 reg; + + /* Reset PHYs */ + usb_phy_reset(dwc->usb2_phy); + + if (dwc->maximum_speed == USB_SPEED_SUPER) + usb_phy_reset(dwc->usb3_phy); + + /* Initialize PHYs */ + ret = dwc3_init_usb_phys(dwc); + if (ret) { + pr_err("%s: dwc3_init_phys returned %d\n", + __func__, ret); + return ret; + } + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); - reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST; - dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + reg &= ~DWC3_GUSB3PIPECTL_DELAYP1TRANS; - /* Clear USB2 PHY reset */ - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + /* core exits U1/U2/U3 only in PHY power state P1/P2/P3 respectively */ + if (dwc->revision <= DWC3_REVISION_310A) + reg |= DWC3_GUSB3PIPECTL_UX_EXIT_IN_PX; - mdelay(100); + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); - /* After PHYs are stable we can take Core out of reset state */ - reg = dwc3_readl(dwc->regs, DWC3_GCTL); - reg &= ~DWC3_GCTL_CORESOFTRESET; - dwc3_writel(dwc->regs, DWC3_GCTL, reg); + dwc3_notify_event(dwc, DWC3_CONTROLLER_RESET_EVENT, 0); + + dwc3_notify_event(dwc, DWC3_CONTROLLER_POST_RESET_EVENT, 0); return 0; } @@ -190,7 +265,7 @@ static void dwc3_free_one_event_buffer(struct dwc3 *dwc, * otherwise ERR_PTR(errno). */ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc, - unsigned length) + unsigned length, enum event_buf_type type) { struct dwc3_event_buffer *evt; @@ -200,6 +275,7 @@ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc, evt->dwc = dwc; evt->length = length; + evt->type = type; evt->buf = dma_alloc_coherent(dwc->dev, length, &evt->dma, GFP_KERNEL); if (!evt->buf) @@ -234,26 +310,40 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc) */ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) { - int num; - int i; + int i; + int j = 0; - num = DWC3_NUM_INT(dwc->hwparams.hwparams1); - dwc->num_event_buffers = num; + dwc->num_event_buffers = dwc->num_normal_event_buffers + + dwc->num_gsi_event_buffers; - dwc->ev_buffs = devm_kzalloc(dwc->dev, sizeof(*dwc->ev_buffs) * num, + dwc->ev_buffs = devm_kzalloc(dwc->dev, + sizeof(*dwc->ev_buffs) * dwc->num_event_buffers, GFP_KERNEL); if (!dwc->ev_buffs) return -ENOMEM; - for (i = 0; i < num; i++) { + for (i = 0; i < dwc->num_normal_event_buffers; i++) { struct dwc3_event_buffer *evt; - evt = dwc3_alloc_one_event_buffer(dwc, length); + evt = dwc3_alloc_one_event_buffer(dwc, length, + EVT_BUF_TYPE_NORMAL); if (IS_ERR(evt)) { dev_err(dwc->dev, "can't allocate event buffer\n"); return PTR_ERR(evt); } - dwc->ev_buffs[i] = evt; + dwc->ev_buffs[j++] = evt; + } + + for (i = 0; i < dwc->num_gsi_event_buffers; i++) { + struct dwc3_event_buffer *evt; + + evt = dwc3_alloc_one_event_buffer(dwc, length, + EVT_BUF_TYPE_GSI); + if (IS_ERR(evt)) { + dev_err(dwc->dev, "can't allocate event buffer\n"); + return PTR_ERR(evt); + } + dwc->ev_buffs[j++] = evt; } return 0; @@ -265,25 +355,40 @@ 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; int n; for (n = 0; n < dwc->num_event_buffers; n++) { evt = dwc->ev_buffs[n]; - dev_dbg(dwc->dev, "Event buf %p dma %08llx length %d\n", + dev_dbg(dwc->dev, "Event buf %pK dma %08llx length %d\n", evt->buf, (unsigned long long) evt->dma, evt->length); + memset(evt->buf, 0, evt->length); + evt->lpos = 0; dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), lower_32_bits(evt->dma)); - dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), - upper_32_bits(evt->dma)); - dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), - DWC3_GEVNTSIZ_SIZE(evt->length)); + + if (evt->type == EVT_BUF_TYPE_NORMAL) { + dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), + upper_32_bits(evt->dma)); + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), + DWC3_GEVNTSIZ_SIZE(evt->length)); + } else { + dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), + DWC3_GEVNTADRHI_EVNTADRHI_GSI_EN( + DWC3_GEVENT_TYPE_GSI) | + DWC3_GEVNTADRHI_EVNTADRHI_GSI_IDX(n)); + + dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), + DWC3_GEVNTCOUNT_EVNTINTRPTMASK | + ((evt->length) & 0xffff)); + } + dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0); } @@ -529,7 +634,7 @@ static int dwc3_phy_setup(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 hwparams4 = dwc->hwparams.hwparams4; u32 reg; @@ -559,16 +664,28 @@ static int dwc3_core_init(struct dwc3 *dwc) /* Handle USB2.0-only core configuration */ if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == DWC3_GHWPARAMS3_SSPHY_IFC_DIS) { - if (dwc->maximum_speed == USB_SPEED_SUPER) - dwc->maximum_speed = USB_SPEED_HIGH; + if (dwc->max_hw_supp_speed == USB_SPEED_SUPER) { + dwc->max_hw_supp_speed = USB_SPEED_HIGH; + dwc->maximum_speed = dwc->max_hw_supp_speed; + } } - /* issue device SoftReset too */ - ret = dwc3_soft_reset(dwc); + /* + * Workaround for STAR 9000961433 which affects only version + * 3.00a of the DWC_usb3 core. This prevents the controller + * interrupt from being masked while handling events. IMOD + * allows us to work around this issue. Enable it for the + * affected version. + */ + if (!dwc->imod_interval && (dwc->revision == DWC3_REVISION_300A)) + dwc->imod_interval = 1; + + ret = dwc3_core_reset(dwc); if (ret) goto err0; - ret = dwc3_core_soft_reset(dwc); + /* issue device SoftReset too */ + ret = dwc3_soft_reset(dwc); if (ret) goto err0; @@ -639,6 +756,15 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_core_num_eps(dwc); + /* + * Disable clock gating to work around a known HW bug that causes the + * internal RAM clock to get stuck when entering low power modes. + */ + if (dwc->disable_clk_gating) { + dev_dbg(dwc->dev, "Disabling controller clock gating.\n"); + reg |= DWC3_GCTL_DSBLCLKGTNG; + } + dwc3_writel(dwc->regs, DWC3_GCTL, reg); ret = dwc3_alloc_scratch_buffers(dwc); @@ -649,6 +775,17 @@ static int dwc3_core_init(struct dwc3 *dwc) if (ret) goto err2; + /* + * clear Elastic buffer mode in GUSBPIPE_CTRL(0) register, otherwise + * it results in high link errors and could cause SS mode transfer + * failure. + */ + if (!dwc->nominal_elastic_buffer) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); + reg &= ~DWC3_GUSB3PIPECTL_ELASTIC_BUF_MODE; + dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + } + return 0; err2: @@ -743,38 +880,16 @@ static int dwc3_core_get_phy(struct dwc3 *dwc) static int dwc3_core_init_mode(struct dwc3 *dwc) { struct device *dev = dwc->dev; - int ret; switch (dwc->dr_mode) { case USB_DR_MODE_PERIPHERAL: dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); - ret = dwc3_gadget_init(dwc); - if (ret) { - dev_err(dev, "failed to initialize gadget\n"); - return ret; - } break; case USB_DR_MODE_HOST: dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); - ret = dwc3_host_init(dwc); - if (ret) { - dev_err(dev, "failed to initialize host\n"); - return ret; - } break; case USB_DR_MODE_OTG: dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); - ret = dwc3_host_init(dwc); - if (ret) { - dev_err(dev, "failed to initialize host\n"); - return ret; - } - - ret = dwc3_gadget_init(dwc); - if (ret) { - dev_err(dev, "failed to initialize gadget\n"); - return ret; - } break; default: dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode); @@ -806,8 +921,107 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc) dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); } +/* XHCI reset, resets other CORE registers as well, re-init those */ +void dwc3_post_host_reset_core_init(struct dwc3 *dwc) +{ + dwc3_core_init(dwc); + dwc3_gadget_restart(dwc); +} + +static void (*notify_event)(struct dwc3 *, unsigned, unsigned); +void dwc3_set_notifier(void (*notify)(struct dwc3 *, unsigned, unsigned)) +{ + notify_event = notify; +} +EXPORT_SYMBOL(dwc3_set_notifier); + +int dwc3_notify_event(struct dwc3 *dwc, unsigned event, unsigned value) +{ + int ret = 0; + + if (dwc->notify_event) + dwc->notify_event(dwc, event, value); + else + ret = -ENODEV; + + return ret; +} +EXPORT_SYMBOL(dwc3_notify_event); + +int dwc3_core_pre_init(struct dwc3 *dwc) +{ + int ret; + + dwc3_cache_hwparams(dwc); + + ret = dwc3_phy_setup(dwc); + if (ret) + goto err0; + + if (!dwc->ev_buffs) { + ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE); + if (ret) { + dev_err(dwc->dev, "failed to allocate event buffers\n"); + ret = -ENOMEM; + goto err1; + } + } + + ret = dwc3_core_init(dwc); + if (ret) { + dev_err(dwc->dev, "failed to initialize core\n"); + goto err2; + } + + ret = phy_power_on(dwc->usb2_generic_phy); + if (ret < 0) + goto err3; + + ret = phy_power_on(dwc->usb3_generic_phy); + if (ret < 0) + goto err4; + + ret = dwc3_event_buffers_setup(dwc); + if (ret) { + dev_err(dwc->dev, "failed to setup event buffers\n"); + goto err5; + } + + ret = dwc3_core_init_mode(dwc); + if (ret) { + dev_err(dwc->dev, "failed to set mode with dwc3 core\n"); + goto err6; + } + + return ret; + +err6: + dwc3_event_buffers_cleanup(dwc); +err5: + phy_power_off(dwc->usb3_generic_phy); +err4: + phy_power_off(dwc->usb2_generic_phy); +err3: + dwc3_core_exit(dwc); +err2: + dwc3_free_event_buffers(dwc); +err1: + dwc3_ulpi_exit(dwc); +err0: + return ret; +} + #define DWC3_ALIGN_MASK (16 - 1) +/* check whether the core supports IMOD */ +bool dwc3_has_imod(struct dwc3 *dwc) +{ + return ((dwc3_is_usb3(dwc) && + dwc->revision >= DWC3_REVISION_300A) || + (dwc3_is_usb31(dwc) && + dwc->revision >= DWC3_USB31_REVISION_120A)); +} + static int dwc3_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -818,7 +1032,8 @@ static int dwc3_probe(struct platform_device *pdev) u8 tx_de_emphasis; u8 hird_threshold; u32 fladj = 0; - + u32 num_evt_buffs; + int irq; int ret; void __iomem *regs; @@ -832,6 +1047,7 @@ static int dwc3_probe(struct platform_device *pdev) dwc->mem = mem; dwc->dev = dev; + dwc->notify_event = notify_event; res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { dev_err(dev, "missing IRQ\n"); @@ -842,12 +1058,27 @@ static int dwc3_probe(struct platform_device *pdev) dwc->xhci_resources[1].flags = res->flags; dwc->xhci_resources[1].name = res->name; + irq = platform_get_irq(to_platform_device(dwc->dev), 0); + + /* will be enabled in dwc3_msm_resume() */ + irq_set_status_flags(irq, IRQ_NOAUTOEN); + ret = devm_request_irq(dev, irq, dwc3_interrupt, IRQF_SHARED, "dwc3", + dwc); + if (ret) { + dev_err(dwc->dev, "failed to request irq #%d --> %d\n", + irq, ret); + return -ENODEV; + } + + dwc->irq = irq; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "missing memory resource\n"); return -ENODEV; } + dwc->reg_phys = res->start; dwc->xhci_resources[0].start = res->start; dwc->xhci_resources[0].end = dwc->xhci_resources[0].start + DWC3_XHCI_REGS_END; @@ -882,6 +1113,7 @@ static int dwc3_probe(struct platform_device *pdev) hird_threshold = 12; dwc->maximum_speed = usb_get_maximum_speed(dev); + dwc->max_hw_supp_speed = dwc->maximum_speed; dwc->dr_mode = usb_get_dr_mode(dev); dwc->has_lpm_erratum = device_property_read_bool(dev, @@ -930,8 +1162,32 @@ static int dwc3_probe(struct platform_device *pdev) device_property_read_u32(dev, "snps,quirk-frame-length-adjustment", &fladj); + dwc->nominal_elastic_buffer = device_property_read_bool(dev, + "snps,nominal-elastic-buffer"); + dwc->usb3_u1u2_disable = device_property_read_bool(dev, + "snps,usb3-u1u2-disable"); + dwc->disable_clk_gating = device_property_read_bool(dev, + "snps,disable-clk-gating"); + dwc->enable_bus_suspend = device_property_read_bool(dev, + "snps,bus-suspend-enable"); + + dwc->num_normal_event_buffers = 1; + ret = device_property_read_u32(dev, + "snps,num-normal-evt-buffs", &num_evt_buffs); + if (!ret) + dwc->num_normal_event_buffers = num_evt_buffs; + + ret = device_property_read_u32(dev, + "snps,num-gsi-evt-buffs", &dwc->num_gsi_event_buffers); + + if (dwc->enable_bus_suspend) { + pm_runtime_set_autosuspend_delay(dev, 500); + pm_runtime_use_autosuspend(dev); + } + if (pdata) { dwc->maximum_speed = pdata->maximum_speed; + dwc->max_hw_supp_speed = dwc->maximum_speed; dwc->has_lpm_erratum = pdata->has_lpm_erratum; if (pdata->lpm_nyet_threshold) lpm_nyet_threshold = pdata->lpm_nyet_threshold; @@ -965,7 +1221,7 @@ static int dwc3_probe(struct platform_device *pdev) /* default to superspeed if no maximum_speed passed */ if (dwc->maximum_speed == USB_SPEED_UNKNOWN) - dwc->maximum_speed = USB_SPEED_SUPER; + dwc->max_hw_supp_speed = dwc->maximum_speed = USB_SPEED_SUPER; dwc->lpm_nyet_threshold = lpm_nyet_threshold; dwc->tx_de_emphasis = tx_de_emphasis; @@ -973,104 +1229,84 @@ static int dwc3_probe(struct platform_device *pdev) dwc->hird_threshold = hird_threshold | (dwc->is_utmi_l1_suspend << 4); + init_waitqueue_head(&dwc->wait_linkstate); platform_set_drvdata(pdev, dwc); - dwc3_cache_hwparams(dwc); - - ret = dwc3_phy_setup(dwc); - if (ret) - goto err0; - ret = dwc3_core_get_phy(dwc); if (ret) goto err0; spin_lock_init(&dwc->lock); - if (!dev->dma_mask) { - dev->dma_mask = dev->parent->dma_mask; - dev->dma_parms = dev->parent->dma_parms; - dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask); + dev->dma_mask = dev->parent->dma_mask; + dev->dma_parms = dev->parent->dma_parms; + dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask); + + dwc->dwc_wq = alloc_ordered_workqueue("dwc_wq", WQ_HIGHPRI); + if (!dwc->dwc_wq) { + pr_err("%s: Unable to create workqueue dwc_wq\n", __func__); + return -ENOMEM; } + INIT_WORK(&dwc->bh_work, dwc3_bh_work); + + pm_runtime_no_callbacks(dev); + pm_runtime_set_active(dev); pm_runtime_enable(dev); - pm_runtime_get_sync(dev); pm_runtime_forbid(dev); - ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE); - if (ret) { - dev_err(dwc->dev, "failed to allocate event buffers\n"); - ret = -ENOMEM; - goto err1; - } - if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) dwc->dr_mode = USB_DR_MODE_HOST; else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) dwc->dr_mode = USB_DR_MODE_PERIPHERAL; - if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) + if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) { dwc->dr_mode = USB_DR_MODE_OTG; - - ret = dwc3_core_init(dwc); - if (ret) { - dev_err(dev, "failed to initialize core\n"); - goto err1; + dwc->is_drd = true; } /* Adjust Frame Length */ dwc3_frame_length_adjustment(dwc, fladj); - 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; + /* Hardcode number of eps */ + dwc->num_in_eps = 16; + dwc->num_out_eps = 16; - ret = dwc3_event_buffers_setup(dwc); - if (ret) { - dev_err(dwc->dev, "failed to setup event buffers\n"); - goto err4; + if (dwc->dr_mode == USB_DR_MODE_OTG || + dwc->dr_mode == USB_DR_MODE_PERIPHERAL) { + ret = dwc3_gadget_init(dwc); + if (ret) { + dev_err(dev, "failed to initialize gadget\n"); + goto err0; + } } - ret = dwc3_core_init_mode(dwc); - if (ret) - goto err5; + if (dwc->dr_mode == USB_DR_MODE_OTG || + dwc->dr_mode == USB_DR_MODE_HOST) { + ret = dwc3_host_init(dwc); + if (ret) { + dev_err(dev, "failed to initialize host\n"); + goto err_gadget; + } + } ret = dwc3_debugfs_init(dwc); if (ret) { dev_err(dev, "failed to initialize debugfs\n"); - goto err6; + goto err_host; } pm_runtime_allow(dev); return 0; -err6: - dwc3_core_exit_mode(dwc); - -err5: - dwc3_event_buffers_cleanup(dwc); - -err4: - phy_power_off(dwc->usb3_generic_phy); - -err3: - phy_power_off(dwc->usb2_generic_phy); - -err2: - usb_phy_set_suspend(dwc->usb2_phy, 1); - usb_phy_set_suspend(dwc->usb3_phy, 1); - dwc3_core_exit(dwc); - -err1: - dwc3_free_event_buffers(dwc); - dwc3_ulpi_exit(dwc); - +err_host: + if (dwc->dr_mode == USB_DR_MODE_OTG || + dwc->dr_mode == USB_DR_MODE_HOST) + dwc3_host_exit(dwc); +err_gadget: + if (dwc->dr_mode == USB_DR_MODE_OTG || + dwc->dr_mode == USB_DR_MODE_PERIPHERAL) + dwc3_gadget_exit(dwc); err0: /* * restore res->start back to its original value so that, in case the @@ -1078,6 +1314,7 @@ err0: * memory region the next time probe is called. */ res->start -= DWC3_GLOBALS_REGS_START; + destroy_workqueue(dwc->dwc_wq); return ret; } @@ -1099,14 +1336,14 @@ static int dwc3_remove(struct platform_device *pdev) dwc3_event_buffers_cleanup(dwc); dwc3_free_event_buffers(dwc); - usb_phy_set_suspend(dwc->usb2_phy, 1); - usb_phy_set_suspend(dwc->usb3_phy, 1); phy_power_off(dwc->usb2_generic_phy); phy_power_off(dwc->usb3_generic_phy); dwc3_core_exit(dwc); dwc3_ulpi_exit(dwc); + destroy_workqueue(dwc->dwc_wq); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); @@ -1119,6 +1356,10 @@ static int dwc3_suspend(struct device *dev) struct dwc3 *dwc = dev_get_drvdata(dev); unsigned long flags; + /* Check if platform glue driver handling PM, if not then handle here */ + if (!dwc3_notify_event(dwc, DWC3_CORE_PM_SUSPEND_EVENT, 0)) + return 0; + spin_lock_irqsave(&dwc->lock, flags); switch (dwc->dr_mode) { @@ -1151,6 +1392,10 @@ static int dwc3_resume(struct device *dev) unsigned long flags; int ret; + /* Check if platform glue driver handling PM, if not then handle here */ + if (!dwc3_notify_event(dwc, DWC3_CORE_PM_RESUME_EVENT, 0)) + return 0; + pinctrl_pm_select_default_state(dev); usb_phy_init(dwc->usb3_phy); @@ -1193,8 +1438,26 @@ err_usb2phy_init: return ret; } +static int dwc3_pm_restore(struct device *dev) +{ + /* + * Set the core as runtime active to prevent the runtime + * PM ops being called before the PM restore is completed. + */ + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return 0; +} + static const struct dev_pm_ops dwc3_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume) + .suspend = dwc3_suspend, + .resume = dwc3_resume, + .freeze = dwc3_suspend, + .thaw = dwc3_pm_restore, + .poweroff = dwc3_suspend, + .restore = dwc3_pm_restore, }; #define DWC3_PM_OPS &(dwc3_dev_pm_ops)