#include <linux/of.h>
#include <linux/acpi.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/irq.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
/* -------------------------------------------------------------------------- */
+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;
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;
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;
}
* 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;
evt->dwc = dwc;
evt->length = length;
+ evt->type = type;
evt->buf = dma_alloc_coherent(dwc->dev, length,
&evt->dma, GFP_KERNEL);
if (!evt->buf)
*/
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;
*
* 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);
}
*
* 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;
/* 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;
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);
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:
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);
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;
u8 tx_de_emphasis;
u8 hird_threshold;
u32 fladj = 0;
-
+ u32 num_evt_buffs;
+ int irq;
int ret;
void __iomem *regs;
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");
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;
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,
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;
/* 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;
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
* memory region the next time probe is called.
*/
res->start -= DWC3_GLOBALS_REGS_START;
+ destroy_workqueue(dwc->dwc_wq);
return ret;
}
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);
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) {
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);
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)