#include <linux/usb/of.h>
#include <linux/usb/otg.h>
-#include "platform_data.h"
#include "core.h"
#include "gadget.h"
#include "io.h"
#include "debug.h"
-/* -------------------------------------------------------------------------- */
+#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
{
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
}
+u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type)
+{
+ struct dwc3 *dwc = dep->dwc;
+ u32 reg;
+
+ dwc3_writel(dwc->regs, DWC3_GDBGFIFOSPACE,
+ DWC3_GDBGFIFOSPACE_NUM(dep->number) |
+ DWC3_GDBGFIFOSPACE_TYPE(type));
+
+ reg = dwc3_readl(dwc->regs, DWC3_GDBGFIFOSPACE);
+
+ return DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(reg);
+}
+
/**
* dwc3_core_soft_reset - Issues core soft reset and PHY reset
* @dwc: pointer to our context structure
static int dwc3_core_soft_reset(struct dwc3 *dwc)
{
u32 reg;
+ int retries = 1000;
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);
-
- /* Assert USB3 PHY reset */
- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
- reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST;
- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
-
- /* Assert USB2 PHY reset */
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
-
usb_phy_init(dwc->usb2_phy);
usb_phy_init(dwc->usb3_phy);
ret = phy_init(dwc->usb2_generic_phy);
phy_exit(dwc->usb2_generic_phy);
return ret;
}
- mdelay(100);
- /* Clear USB3 PHY reset */
- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
- reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST;
- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+ /*
+ * We're resetting only the device side because, if we're in host mode,
+ * XHCI driver will reset the host block. If dwc3 was configured for
+ * host-only mode, then we can return early.
+ */
+ if (dwc->dr_mode == USB_DR_MODE_HOST)
+ return 0;
- /* Clear USB2 PHY reset */
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ reg |= DWC3_DCTL_CSFTRST;
+ dwc3_writel(dwc->regs, DWC3_DCTL, reg);
- mdelay(100);
+ do {
+ reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+ if (!(reg & DWC3_DCTL_CSFTRST))
+ return 0;
- /* 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);
+ udelay(1);
+ } while (--retries);
- return 0;
+ return -ETIMEDOUT;
}
/**
/*
* dwc3_frame_length_adjustment - Adjusts frame length if required
* @dwc3: Pointer to our controller context structure
- * @fladj: Value of GFLADJ_30MHZ to adjust frame length
*/
-static void dwc3_frame_length_adjustment(struct dwc3 *dwc, u32 fladj)
+static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
{
u32 reg;
u32 dft;
if (dwc->revision < DWC3_REVISION_250A)
return;
- if (fladj == 0)
+ if (dwc->fladj == 0)
return;
reg = dwc3_readl(dwc->regs, DWC3_GFLADJ);
dft = reg & DWC3_GFLADJ_30MHZ_MASK;
- if (!dev_WARN_ONCE(dwc->dev, dft == fladj,
+ if (!dev_WARN_ONCE(dwc->dev, dft == dwc->fladj,
"request value same as default, ignoring\n")) {
reg &= ~DWC3_GFLADJ_30MHZ_MASK;
- reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | fladj;
+ reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj;
dwc3_writel(dwc->regs, DWC3_GFLADJ, reg);
}
}
static void dwc3_free_event_buffers(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
- int i;
- for (i = 0; i < dwc->num_event_buffers; i++) {
- evt = dwc->ev_buffs[i];
- if (evt)
- dwc3_free_one_event_buffer(dwc, evt);
- }
+ evt = dwc->ev_buf;
+ if (evt)
+ dwc3_free_one_event_buffer(dwc, evt);
}
/**
*/
static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
{
- int num;
- int i;
-
- num = DWC3_NUM_INT(dwc->hwparams.hwparams1);
- dwc->num_event_buffers = num;
-
- dwc->ev_buffs = devm_kzalloc(dwc->dev, sizeof(*dwc->ev_buffs) * num,
- GFP_KERNEL);
- if (!dwc->ev_buffs)
- return -ENOMEM;
-
- for (i = 0; i < num; i++) {
- struct dwc3_event_buffer *evt;
+ struct dwc3_event_buffer *evt;
- evt = dwc3_alloc_one_event_buffer(dwc, length);
- if (IS_ERR(evt)) {
- dev_err(dwc->dev, "can't allocate event buffer\n");
- return PTR_ERR(evt);
- }
- dwc->ev_buffs[i] = evt;
+ evt = dwc3_alloc_one_event_buffer(dwc, length);
+ if (IS_ERR(evt)) {
+ dev_err(dwc->dev, "can't allocate event buffer\n");
+ return PTR_ERR(evt);
}
+ dwc->ev_buf = evt;
return 0;
}
static 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",
- evt->buf, (unsigned long long) evt->dma,
- 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));
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 0);
- }
+
+ evt = dwc->ev_buf;
+ dwc3_trace(trace_dwc3_core,
+ "Event buf %p dma %08llx length %d\n",
+ evt->buf, (unsigned long long) evt->dma,
+ evt->length);
+
+ evt->lpos = 0;
+
+ dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(0),
+ lower_32_bits(evt->dma));
+ dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(0),
+ upper_32_bits(evt->dma));
+ dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0),
+ DWC3_GEVNTSIZ_SIZE(evt->length));
+ dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0);
return 0;
}
static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
{
struct dwc3_event_buffer *evt;
- int n;
- for (n = 0; n < dwc->num_event_buffers; n++) {
- evt = dwc->ev_buffs[n];
+ evt = dwc->ev_buf;
- evt->lpos = 0;
+ evt->lpos = 0;
- dwc3_writel(dwc->regs, DWC3_GEVNTADRLO(n), 0);
- dwc3_writel(dwc->regs, DWC3_GEVNTADRHI(n), 0);
- dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(n), DWC3_GEVNTSIZ_INTMASK
- | DWC3_GEVNTSIZ_SIZE(0));
- dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(n), 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);
}
static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc)
if (dwc->u2ss_inp3_quirk)
reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK;
+ if (dwc->dis_rxdet_inp3_quirk)
+ reg |= DWC3_GUSB3PIPECTL_DISRXDETINP3;
+
if (dwc->req_p1p2p3_quirk)
reg |= DWC3_GUSB3PIPECTL_REQP1P2P3;
if (dwc->dis_u3_susphy_quirk)
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
+ if (dwc->dis_del_phy_power_chg_quirk)
+ reg &= ~DWC3_GUSB3PIPECTL_DEPOCHANGE;
+
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
break;
}
+ switch (dwc->hsphy_mode) {
+ case USBPHY_INTERFACE_MODE_UTMI:
+ reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
+ DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
+ reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_8_BIT) |
+ DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_8_BIT);
+ break;
+ case USBPHY_INTERFACE_MODE_UTMIW:
+ reg &= ~(DWC3_GUSB2PHYCFG_PHYIF_MASK |
+ DWC3_GUSB2PHYCFG_USBTRDTIM_MASK);
+ reg |= DWC3_GUSB2PHYCFG_PHYIF(UTMI_PHYIF_16_BIT) |
+ DWC3_GUSB2PHYCFG_USBTRDTIM(USBTRDTIM_UTMI_16_BIT);
+ break;
+ default:
+ break;
+ }
+
/*
* Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to
* '0' during coreConsultant configuration. So default value will
if (dwc->dis_enblslpm_quirk)
reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM;
+ if (dwc->dis_u2_freeclk_exists_quirk)
+ reg &= ~DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS;
+
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
return 0;
}
+static void dwc3_core_exit(struct dwc3 *dwc)
+{
+ dwc3_event_buffers_cleanup(dwc);
+
+ usb_phy_shutdown(dwc->usb2_phy);
+ usb_phy_shutdown(dwc->usb3_phy);
+ phy_exit(dwc->usb2_generic_phy);
+ phy_exit(dwc->usb3_generic_phy);
+
+ 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_init - Low-level initialization of DWC3 Core
* @dwc: Pointer to our controller context structure
if (ret)
goto err0;
+ ret = dwc3_phy_setup(dwc);
+ if (ret)
+ goto err0;
+
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
reg |= DWC3_GCTL_GBLHIBERNATIONEN;
break;
default:
- dev_dbg(dwc->dev, "No power optimization available\n");
+ dwc3_trace(trace_dwc3_core, "No power optimization available\n");
}
/* check if current dwc3 is on simulation board */
if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) {
- dev_dbg(dwc->dev, "it is on FPGA board\n");
+ dwc3_trace(trace_dwc3_core,
+ "running on FPGA platform\n");
dwc->is_fpga = true;
}
if (dwc->revision < DWC3_REVISION_190A)
reg |= DWC3_GCTL_U2RSTECN;
- dwc3_core_num_eps(dwc);
-
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
- ret = dwc3_alloc_scratch_buffers(dwc);
- if (ret)
- goto err1;
+ dwc3_core_num_eps(dwc);
ret = dwc3_setup_scratch_buffers(dwc);
if (ret)
+ goto err1;
+
+ /* Adjust Frame Length */
+ dwc3_frame_length_adjustment(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;
+ }
+
+ switch (dwc->dr_mode) {
+ case USB_DR_MODE_PERIPHERAL:
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
+ break;
+ case USB_DR_MODE_HOST:
+ dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
+ break;
+ default:
+ dev_dbg(dwc->dev, "Unsupported mode %d\n", dwc->dr_mode);
+ break;
+ }
+
return 0;
+err4:
+ phy_power_off(dwc->usb2_generic_phy);
+
+err3:
+ phy_power_off(dwc->usb3_generic_phy);
+
err2:
- dwc3_free_scratch_buffers(dwc);
+ usb_phy_set_suspend(dwc->usb2_phy, 1);
+ usb_phy_set_suspend(dwc->usb3_phy, 1);
+ dwc3_core_exit(dwc);
err1:
usb_phy_shutdown(dwc->usb2_phy);
return ret;
}
-static void dwc3_core_exit(struct dwc3 *dwc)
-{
- dwc3_free_scratch_buffers(dwc);
- usb_phy_shutdown(dwc->usb2_phy);
- usb_phy_shutdown(dwc->usb3_phy);
- phy_exit(dwc->usb2_generic_phy);
- phy_exit(dwc->usb3_generic_phy);
-}
-
static int dwc3_core_get_phy(struct dwc3 *dwc)
{
struct device *dev = dwc->dev;
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
ret = dwc3_gadget_init(dwc);
if (ret) {
- dev_err(dev, "failed to initialize gadget\n");
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to initialize gadget\n");
return ret;
}
break;
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
ret = dwc3_host_init(dwc);
if (ret) {
- dev_err(dev, "failed to initialize host\n");
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to initialize host\n");
return ret;
}
break;
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
ret = dwc3_host_init(dwc);
if (ret) {
- dev_err(dev, "failed to initialize host\n");
+ if (ret != -EPROBE_DEFER)
+ 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");
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "failed to initialize gadget\n");
return ret;
}
break;
static int dwc3_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct dwc3_platform_data *pdata = dev_get_platdata(dev);
struct resource *res;
struct dwc3 *dwc;
u8 lpm_nyet_threshold;
u8 tx_de_emphasis;
u8 hird_threshold;
- u32 fladj = 0;
int ret;
dwc->mem = mem;
dwc->dev = dev;
- res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (!res) {
- dev_err(dev, "missing IRQ\n");
- return -ENODEV;
+ /* Try to set 64-bit DMA first */
+ if (!pdev->dev.dma_mask)
+ /* Platform did not initialize dma_mask */
+ ret = dma_coerce_mask_and_coherent(&pdev->dev,
+ DMA_BIT_MASK(64));
+ else
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+
+ /* If seting 64-bit DMA mask fails, fall back to 32-bit DMA mask */
+ if (ret) {
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
}
- dwc->xhci_resources[1].start = res->start;
- dwc->xhci_resources[1].end = res->end;
- dwc->xhci_resources[1].flags = res->flags;
- dwc->xhci_resources[1].name = res->name;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dwc->maximum_speed = usb_get_maximum_speed(dev);
dwc->dr_mode = usb_get_dr_mode(dev);
+ dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node);
dwc->has_lpm_erratum = device_property_read_bool(dev,
"snps,has-lpm-erratum");
dwc->usb3_lpm_capable = device_property_read_bool(dev,
"snps,usb3_lpm_capable");
- dwc->needs_fifo_resize = device_property_read_bool(dev,
- "tx-fifo-resize");
-
dwc->disable_scramble_quirk = device_property_read_bool(dev,
"snps,disable_scramble_quirk");
dwc->u2exit_lfps_quirk = device_property_read_bool(dev,
"snps,lfps_filter_quirk");
dwc->rx_detect_poll_quirk = device_property_read_bool(dev,
"snps,rx_detect_poll_quirk");
+ dwc->dis_u3_autosuspend_quirk = device_property_read_bool(dev,
+ "snps,dis-u3-autosuspend-quirk");
dwc->dis_u3_susphy_quirk = device_property_read_bool(dev,
"snps,dis_u3_susphy_quirk");
dwc->dis_u2_susphy_quirk = device_property_read_bool(dev,
"snps,dis_u2_susphy_quirk");
dwc->dis_enblslpm_quirk = device_property_read_bool(dev,
"snps,dis_enblslpm_quirk");
+ dwc->dis_rxdet_inp3_quirk = device_property_read_bool(dev,
+ "snps,dis_rxdet_inp3_quirk");
+ dwc->dis_u2_freeclk_exists_quirk = device_property_read_bool(dev,
+ "snps,dis-u2-freeclk-exists-quirk");
+ dwc->dis_del_phy_power_chg_quirk = device_property_read_bool(dev,
+ "snps,dis-del-phy-power-chg-quirk");
+ dwc->xhci_slow_suspend_quirk = device_property_read_bool(dev,
+ "snps,xhci-slow-suspend-quirk");
dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
"snps,tx_de_emphasis_quirk");
device_property_read_string(dev, "snps,hsphy_interface",
&dwc->hsphy_interface);
device_property_read_u32(dev, "snps,quirk-frame-length-adjustment",
- &fladj);
-
- if (pdata) {
- dwc->maximum_speed = pdata->maximum_speed;
- dwc->has_lpm_erratum = pdata->has_lpm_erratum;
- if (pdata->lpm_nyet_threshold)
- lpm_nyet_threshold = pdata->lpm_nyet_threshold;
- dwc->is_utmi_l1_suspend = pdata->is_utmi_l1_suspend;
- if (pdata->hird_threshold)
- hird_threshold = pdata->hird_threshold;
-
- dwc->needs_fifo_resize = pdata->tx_fifo_resize;
- dwc->usb3_lpm_capable = pdata->usb3_lpm_capable;
- dwc->dr_mode = pdata->dr_mode;
-
- dwc->disable_scramble_quirk = pdata->disable_scramble_quirk;
- dwc->u2exit_lfps_quirk = pdata->u2exit_lfps_quirk;
- dwc->u2ss_inp3_quirk = pdata->u2ss_inp3_quirk;
- dwc->req_p1p2p3_quirk = pdata->req_p1p2p3_quirk;
- dwc->del_p1p2p3_quirk = pdata->del_p1p2p3_quirk;
- dwc->del_phy_power_chg_quirk = pdata->del_phy_power_chg_quirk;
- dwc->lfps_filter_quirk = pdata->lfps_filter_quirk;
- dwc->rx_detect_poll_quirk = pdata->rx_detect_poll_quirk;
- dwc->dis_u3_susphy_quirk = pdata->dis_u3_susphy_quirk;
- dwc->dis_u2_susphy_quirk = pdata->dis_u2_susphy_quirk;
- dwc->dis_enblslpm_quirk = pdata->dis_enblslpm_quirk;
-
- dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk;
- if (pdata->tx_de_emphasis)
- tx_de_emphasis = pdata->tx_de_emphasis;
-
- dwc->hsphy_interface = pdata->hsphy_interface;
- fladj = pdata->fladj_value;
- }
+ &dwc->fladj);
/* default to superspeed if no maximum_speed passed */
if (dwc->maximum_speed == USB_SPEED_UNKNOWN)
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;
dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
}
+ pm_runtime_set_active(dev);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_autosuspend_delay(dev, DWC3_DEFAULT_AUTOSUSPEND_DELAY);
pm_runtime_enable(dev);
- pm_runtime_get_sync(dev);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0)
+ goto err1;
+
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;
+ goto err2;
}
- if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
+ if (IS_ENABLED(CONFIG_USB_DWC3_HOST) &&
+ (dwc->dr_mode == USB_DR_MODE_OTG ||
+ dwc->dr_mode == USB_DR_MODE_UNKNOWN))
dwc->dr_mode = USB_DR_MODE_HOST;
- else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
+ else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET) &&
+ (dwc->dr_mode == USB_DR_MODE_OTG ||
+ dwc->dr_mode == USB_DR_MODE_UNKNOWN))
dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
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;
- }
-
- /* 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)
+ ret = dwc3_alloc_scratch_buffers(dwc);
+ if (ret)
goto err3;
- ret = dwc3_event_buffers_setup(dwc);
+ ret = dwc3_core_init(dwc);
if (ret) {
- dev_err(dwc->dev, "failed to setup event buffers\n");
+ dev_err(dev, "failed to initialize core\n");
goto err4;
}
if (ret)
goto err5;
- ret = dwc3_debugfs_init(dwc);
- if (ret) {
- dev_err(dev, "failed to initialize debugfs\n");
- goto err6;
- }
-
- pm_runtime_allow(dev);
+ dwc3_debugfs_init(dwc);
+ pm_runtime_put(dev);
return 0;
-err6:
- dwc3_core_exit_mode(dwc);
-
err5:
dwc3_event_buffers_cleanup(dwc);
err4:
- phy_power_off(dwc->usb3_generic_phy);
+ dwc3_free_scratch_buffers(dwc);
err3:
- phy_power_off(dwc->usb2_generic_phy);
+ dwc3_free_event_buffers(dwc);
+ dwc3_ulpi_exit(dwc);
err2:
- usb_phy_set_suspend(dwc->usb2_phy, 1);
- usb_phy_set_suspend(dwc->usb3_phy, 1);
- dwc3_core_exit(dwc);
+ pm_runtime_allow(&pdev->dev);
err1:
- dwc3_free_event_buffers(dwc);
- dwc3_ulpi_exit(dwc);
+ pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
err0:
/*
struct dwc3 *dwc = platform_get_drvdata(pdev);
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ pm_runtime_get_sync(&pdev->dev);
/*
* restore res->start back to its original value so that, in case the
* probe is deferred, we don't end up getting error in request the
dwc3_debugfs_exit(dwc);
dwc3_core_exit_mode(dwc);
- 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);
pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_allow(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ dwc3_free_event_buffers(dwc);
+ dwc3_free_scratch_buffers(dwc);
+
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-static int dwc3_suspend(struct device *dev)
+#ifdef CONFIG_PM
+static int dwc3_suspend_common(struct dwc3 *dwc)
{
- struct dwc3 *dwc = dev_get_drvdata(dev);
unsigned long flags;
- spin_lock_irqsave(&dwc->lock, flags);
-
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_HOST:
+ 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;
+
+ 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:
- dwc3_event_buffers_cleanup(dwc);
+ /* do nothing */
break;
}
- dwc->gctl = dwc3_readl(dwc->regs, DWC3_GCTL);
- spin_unlock_irqrestore(&dwc->lock, flags);
+ return 0;
+}
- usb_phy_shutdown(dwc->usb3_phy);
- usb_phy_shutdown(dwc->usb2_phy);
- phy_exit(dwc->usb2_generic_phy);
- phy_exit(dwc->usb3_generic_phy);
+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;
+ }
- pinctrl_pm_select_sleep_state(dev);
+ 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_resume(struct device *dev)
+static int dwc3_runtime_resume(struct device *dev)
{
- struct dwc3 *dwc = dev_get_drvdata(dev);
- unsigned long flags;
+ struct dwc3 *dwc = dev_get_drvdata(dev);
int ret;
- pinctrl_pm_select_default_state(dev);
+ device_init_wakeup(dev, false);
- usb_phy_init(dwc->usb3_phy);
- usb_phy_init(dwc->usb2_phy);
- ret = phy_init(dwc->usb2_generic_phy);
- if (ret < 0)
+ ret = dwc3_resume_common(dwc);
+ if (ret)
return ret;
- ret = phy_init(dwc->usb3_generic_phy);
- if (ret < 0)
- goto err_usb2phy_init;
+ 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;
+ }
- spin_lock_irqsave(&dwc->lock, flags);
+ pm_runtime_mark_last_busy(dev);
- dwc3_event_buffers_setup(dwc);
- dwc3_writel(dwc->regs, DWC3_GCTL, dwc->gctl);
+ 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:
case USB_DR_MODE_OTG:
- dwc3_gadget_resume(dwc);
- /* FALLTHROUGH */
+ if (dwc3_runtime_checks(dwc))
+ return -EBUSY;
+ break;
case USB_DR_MODE_HOST:
default:
/* do nothing */
break;
}
- spin_unlock_irqrestore(&dwc->lock, flags);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_autosuspend(dev);
+
+ return 0;
+}
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_PM_SLEEP
+static int dwc3_suspend(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ int ret;
+
+ if (pm_runtime_suspended(dwc->dev))
+ return 0;
+
+ ret = dwc3_suspend_common(dwc);
+ if (ret)
+ return ret;
+
+ pinctrl_pm_select_sleep_state(dev);
+
+ return 0;
+}
+
+static int dwc3_resume(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ int ret;
+
+ if (pm_runtime_suspended(dwc->dev))
+ return 0;
+
+ pinctrl_pm_select_default_state(dev);
+
+ ret = dwc3_resume_common(dwc);
+ if (ret)
+ return ret;
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
return 0;
-
-err_usb2phy_init:
- phy_exit(dwc->usb2_generic_phy);
-
- return ret;
}
+#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)
};
-#define DWC3_PM_OPS &(dwc3_dev_pm_ops)
-#else
-#define DWC3_PM_OPS NULL
-#endif
-
#ifdef CONFIG_OF
static const struct of_device_id of_dwc3_match[] = {
{
.name = "dwc3",
.of_match_table = of_match_ptr(of_dwc3_match),
.acpi_match_table = ACPI_PTR(dwc3_acpi_match),
- .pm = DWC3_PM_OPS,
+ .pm = &dwc3_dev_pm_ops,
},
};