Merge tag 'usb-for-v4.8' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
Felipe writes: usb: patches for v4.8 merge window Here's the big pull request for Peripheral stack and all related drivers. This time around with 109 non-merge commits mostly concentrated on drivers/usb/gadget/udc (41.5%) and drivers/usb/dwc3 (28.1%). There's a big rework on dwc3's transfer handling which gave us almost 3x faster USB3 speeds with Mass Storage on a particular test scenario I measured. We are also removing platform_data from dwc3 after converting all users to built-in properties instead. For the Gadget API, we're just adding tracepoints to aid debugging activities. Other than these, there's the usual set of spelling fixes, minor bug fixes and sparse warnings cleanups.
This commit is contained in:
+237
-197
@@ -41,14 +41,13 @@
|
||||
#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)
|
||||
{
|
||||
@@ -149,9 +148,8 @@ static int dwc3_soft_reset(struct dwc3 *dwc)
|
||||
/*
|
||||
* 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;
|
||||
@@ -159,15 +157,15 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc, u32 fladj)
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -507,6 +505,21 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
|
||||
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
|
||||
@@ -556,6 +569,10 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
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;
|
||||
|
||||
@@ -622,22 +639,45 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -649,15 +689,6 @@ err0:
|
||||
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;
|
||||
@@ -735,7 +766,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
|
||||
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;
|
||||
@@ -743,7 +775,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
|
||||
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;
|
||||
@@ -751,13 +784,15 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
|
||||
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;
|
||||
@@ -793,13 +828,11 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
|
||||
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;
|
||||
|
||||
@@ -814,16 +847,6 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
dwc->mem = mem;
|
||||
dwc->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res) {
|
||||
dev_err(dev, "missing IRQ\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
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) {
|
||||
dev_err(dev, "missing memory resource\n");
|
||||
@@ -909,40 +932,7 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
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->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->dis_rxdet_inp3_quirk = pdata->dis_rxdet_inp3_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);
|
||||
|
||||
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
|
||||
dwc->tx_de_emphasis = tx_de_emphasis;
|
||||
@@ -953,10 +943,6 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
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;
|
||||
@@ -969,29 +955,43 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
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_alloc_scratch_buffers(dwc);
|
||||
if (ret)
|
||||
goto err3;
|
||||
|
||||
ret = dwc3_core_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize core\n");
|
||||
goto err1;
|
||||
goto err4;
|
||||
}
|
||||
|
||||
/* Check the maximum_speed parameter */
|
||||
@@ -1021,31 +1021,12 @@ static int dwc3_probe(struct platform_device *pdev)
|
||||
break;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
ret = dwc3_event_buffers_setup(dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to setup event buffers\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
ret = dwc3_core_init_mode(dwc);
|
||||
if (ret)
|
||||
goto err5;
|
||||
|
||||
dwc3_debugfs_init(dwc);
|
||||
pm_runtime_allow(dev);
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1053,20 +1034,19 @@ 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);
|
||||
|
||||
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);
|
||||
|
||||
err2:
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
|
||||
err1:
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
err0:
|
||||
/*
|
||||
* restore res->start back to its original value so that, in case the
|
||||
@@ -1083,6 +1063,7 @@ static int dwc3_remove(struct platform_device *pdev)
|
||||
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
|
||||
@@ -1092,54 +1073,161 @@ static int dwc3_remove(struct platform_device *pdev)
|
||||
|
||||
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
|
||||
static int dwc3_suspend_common(struct dwc3 *dwc)
|
||||
{
|
||||
unsigned long 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:
|
||||
/* 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);
|
||||
|
||||
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:
|
||||
if (dwc3_runtime_checks(dwc))
|
||||
return -EBUSY;
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
case USB_DR_MODE_OTG:
|
||||
dwc3_gadget_suspend(dwc);
|
||||
/* FALLTHROUGH */
|
||||
case USB_DR_MODE_HOST:
|
||||
default:
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
break;
|
||||
}
|
||||
|
||||
dwc->gctl = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
usb_phy_shutdown(dwc->usb3_phy);
|
||||
usb_phy_shutdown(dwc->usb2_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);
|
||||
WARN_ON(phy_power_off(dwc->usb2_generic_phy) < 0);
|
||||
WARN_ON(phy_power_off(dwc->usb3_generic_phy) < 0);
|
||||
ret = dwc3_suspend_common(dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pinctrl_pm_select_sleep_state(dev);
|
||||
|
||||
@@ -1149,76 +1237,28 @@ static int dwc3_suspend(struct device *dev)
|
||||
static int dwc3_resume(struct device *dev)
|
||||
{
|
||||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
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)
|
||||
ret = dwc3_resume_common(dwc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = phy_power_on(dwc->usb3_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err_usb2phy_power;
|
||||
|
||||
usb_phy_init(dwc->usb3_phy);
|
||||
usb_phy_init(dwc->usb2_phy);
|
||||
ret = phy_init(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err_usb3phy_power;
|
||||
|
||||
ret = phy_init(dwc->usb3_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err_usb2phy_init;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
dwc3_event_buffers_setup(dwc);
|
||||
dwc3_writel(dwc->regs, DWC3_GCTL, dwc->gctl);
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
case USB_DR_MODE_OTG:
|
||||
dwc3_gadget_resume(dwc);
|
||||
/* FALLTHROUGH */
|
||||
case USB_DR_MODE_HOST:
|
||||
default:
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_usb2phy_init:
|
||||
phy_exit(dwc->usb2_generic_phy);
|
||||
|
||||
err_usb3phy_power:
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
|
||||
err_usb2phy_power:
|
||||
phy_power_off(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[] = {
|
||||
{
|
||||
@@ -1250,7 +1290,7 @@ static struct platform_driver dwc3_driver = {
|
||||
.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,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
+46
-13
@@ -86,6 +86,7 @@
|
||||
#define DWC3_GCTL 0xc110
|
||||
#define DWC3_GEVTEN 0xc114
|
||||
#define DWC3_GSTS 0xc118
|
||||
#define DWC3_GUCTL1 0xc11c
|
||||
#define DWC3_GSNPSID 0xc120
|
||||
#define DWC3_GGPIO 0xc124
|
||||
#define DWC3_GUID 0xc128
|
||||
@@ -138,10 +139,12 @@
|
||||
#define DWC3_DGCMDPAR 0xc710
|
||||
#define DWC3_DGCMD 0xc714
|
||||
#define DWC3_DALEPENA 0xc720
|
||||
#define DWC3_DEPCMDPAR2(n) (0xc800 + (n * 0x10))
|
||||
#define DWC3_DEPCMDPAR1(n) (0xc804 + (n * 0x10))
|
||||
#define DWC3_DEPCMDPAR0(n) (0xc808 + (n * 0x10))
|
||||
#define DWC3_DEPCMD(n) (0xc80c + (n * 0x10))
|
||||
|
||||
#define DWC3_DEP_BASE(n) (0xc800 + (n * 0x10))
|
||||
#define DWC3_DEPCMDPAR2 0x00
|
||||
#define DWC3_DEPCMDPAR1 0x04
|
||||
#define DWC3_DEPCMDPAR0 0x08
|
||||
#define DWC3_DEPCMD 0x0c
|
||||
|
||||
/* OTG Registers */
|
||||
#define DWC3_OCFG 0xcc00
|
||||
@@ -231,6 +234,14 @@
|
||||
#define DWC3_GEVNTSIZ_INTMASK (1 << 31)
|
||||
#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff)
|
||||
|
||||
/* Global HWPARAMS0 Register */
|
||||
#define DWC3_GHWPARAMS0_USB3_MODE(n) ((n) & 0x3)
|
||||
#define DWC3_GHWPARAMS0_MBUS_TYPE(n) (((n) >> 3) & 0x7)
|
||||
#define DWC3_GHWPARAMS0_SBUS_TYPE(n) (((n) >> 6) & 0x3)
|
||||
#define DWC3_GHWPARAMS0_MDWIDTH(n) (((n) >> 8) & 0xff)
|
||||
#define DWC3_GHWPARAMS0_SDWIDTH(n) (((n) >> 16) & 0xff)
|
||||
#define DWC3_GHWPARAMS0_AWIDTH(n) (((n) >> 24) & 0xff)
|
||||
|
||||
/* Global HWPARAMS1 Register */
|
||||
#define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24)
|
||||
#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0
|
||||
@@ -260,6 +271,10 @@
|
||||
/* Global HWPARAMS6 Register */
|
||||
#define DWC3_GHWPARAMS6_EN_FPGA (1 << 7)
|
||||
|
||||
/* Global HWPARAMS7 Register */
|
||||
#define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff)
|
||||
#define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff)
|
||||
|
||||
/* Global Frame Length Adjustment Register */
|
||||
#define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7)
|
||||
#define DWC3_GFLADJ_30MHZ_MASK 0x3f
|
||||
@@ -468,6 +483,8 @@ struct dwc3_event_buffer {
|
||||
* @endpoint: usb endpoint
|
||||
* @pending_list: list of pending requests for this endpoint
|
||||
* @started_list: list of started requests on this endpoint
|
||||
* @lock: spinlock for endpoint request queue traversal
|
||||
* @regs: pointer to first endpoint register
|
||||
* @trb_pool: array of transaction buffers
|
||||
* @trb_pool_dma: dma address of @trb_pool
|
||||
* @trb_enqueue: enqueue 'pointer' into TRB array
|
||||
@@ -480,6 +497,8 @@ struct dwc3_event_buffer {
|
||||
* @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
|
||||
* @resource_index: Resource transfer index
|
||||
* @interval: the interval on which the ISOC transfer is started
|
||||
* @allocated_requests: number of requests allocated
|
||||
* @queued_requests: number of requests queued for transfer
|
||||
* @name: a human readable name e.g. ep1out-bulk
|
||||
* @direction: true for TX, false for RX
|
||||
* @stream_capable: true when streams are enabled
|
||||
@@ -489,6 +508,9 @@ struct dwc3_ep {
|
||||
struct list_head pending_list;
|
||||
struct list_head started_list;
|
||||
|
||||
spinlock_t lock;
|
||||
void __iomem *regs;
|
||||
|
||||
struct dwc3_trb *trb_pool;
|
||||
dma_addr_t trb_pool_dma;
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc;
|
||||
@@ -521,6 +543,8 @@ struct dwc3_ep {
|
||||
u8 number;
|
||||
u8 type;
|
||||
u8 resource_index;
|
||||
u32 allocated_requests;
|
||||
u32 queued_requests;
|
||||
u32 interval;
|
||||
|
||||
char name[20];
|
||||
@@ -712,6 +736,8 @@ struct dwc3_scratchpad_array {
|
||||
* @gadget_driver: pointer to the gadget driver
|
||||
* @regs: base address for our registers
|
||||
* @regs_size: address space size
|
||||
* @fladj: frame length adjustment
|
||||
* @irq_gadget: peripheral controller's IRQ number
|
||||
* @nr_scratch: number of scratch buffers
|
||||
* @u1u2: only used on revisions <1.83a for workaround
|
||||
* @maximum_speed: maximum speed requested (mainly for testing purposes)
|
||||
@@ -744,6 +770,7 @@ struct dwc3_scratchpad_array {
|
||||
* @lpm_nyet_threshold: LPM NYET response threshold
|
||||
* @hird_threshold: HIRD threshold
|
||||
* @hsphy_interface: "utmi" or "ulpi"
|
||||
* @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
|
||||
* @ep0_expect_in: true when we expect a DATA IN transfer
|
||||
@@ -754,6 +781,7 @@ struct dwc3_scratchpad_array {
|
||||
* 0 - utmi_sleep_n
|
||||
* 1 - utmi_l1_suspend_n
|
||||
* @is_fpga: true when we are using the FPGA board
|
||||
* @pending_events: true when we have pending IRQs to be handled
|
||||
* @pullups_connected: true when Run/Stop bit is set
|
||||
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
|
||||
* @start_config_issued: true when StartConfig command has been issued
|
||||
@@ -818,10 +846,8 @@ struct dwc3 {
|
||||
|
||||
enum usb_dr_mode dr_mode;
|
||||
|
||||
/* used for suspend/resume */
|
||||
u32 dcfg;
|
||||
u32 gctl;
|
||||
|
||||
u32 fladj;
|
||||
u32 irq_gadget;
|
||||
u32 nr_scratch;
|
||||
u32 u1u2;
|
||||
u32 maximum_speed;
|
||||
@@ -860,7 +886,7 @@ struct dwc3 {
|
||||
* just so dwc31 revisions are always larger than dwc3.
|
||||
*/
|
||||
#define DWC3_REVISION_IS_DWC31 0x80000000
|
||||
#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_USB31)
|
||||
#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31)
|
||||
|
||||
enum dwc3_ep0_next ep0_next_event;
|
||||
enum dwc3_ep0_state ep0state;
|
||||
@@ -890,6 +916,7 @@ struct dwc3 {
|
||||
|
||||
const char *hsphy_interface;
|
||||
|
||||
unsigned connected:1;
|
||||
unsigned delayed_status:1;
|
||||
unsigned ep0_bounced:1;
|
||||
unsigned ep0_expect_in:1;
|
||||
@@ -897,6 +924,7 @@ struct dwc3 {
|
||||
unsigned has_lpm_erratum:1;
|
||||
unsigned is_utmi_l1_suspend:1;
|
||||
unsigned is_fpga:1;
|
||||
unsigned pending_events:1;
|
||||
unsigned pullups_connected:1;
|
||||
unsigned setup_packet_pending:1;
|
||||
unsigned three_stage_setup:1;
|
||||
@@ -1094,8 +1122,8 @@ void dwc3_gadget_exit(struct dwc3 *dwc);
|
||||
int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
|
||||
int dwc3_gadget_get_link_state(struct dwc3 *dwc);
|
||||
int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
|
||||
int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
|
||||
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);
|
||||
#else
|
||||
static inline int dwc3_gadget_init(struct dwc3 *dwc)
|
||||
@@ -1110,8 +1138,8 @@ static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc,
|
||||
enum dwc3_link_state state)
|
||||
{ return 0; }
|
||||
|
||||
static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
|
||||
unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
|
||||
static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
|
||||
struct dwc3_gadget_ep_cmd_params *params)
|
||||
{ return 0; }
|
||||
static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
|
||||
int cmd, u32 param)
|
||||
@@ -1122,6 +1150,7 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
|
||||
#if !IS_ENABLED(CONFIG_USB_DWC3_HOST)
|
||||
int dwc3_gadget_suspend(struct dwc3 *dwc);
|
||||
int dwc3_gadget_resume(struct dwc3 *dwc);
|
||||
void dwc3_gadget_process_pending_events(struct dwc3 *dwc);
|
||||
#else
|
||||
static inline int dwc3_gadget_suspend(struct dwc3 *dwc)
|
||||
{
|
||||
@@ -1132,6 +1161,10 @@ static inline int dwc3_gadget_resume(struct dwc3 *dwc)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void dwc3_gadget_process_pending_events(struct dwc3 *dwc)
|
||||
{
|
||||
}
|
||||
#endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC3_ULPI)
|
||||
|
||||
+118
-22
@@ -128,56 +128,112 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state)
|
||||
* dwc3_gadget_event_string - returns event name
|
||||
* @event: the event code
|
||||
*/
|
||||
static inline const char *dwc3_gadget_event_string(u8 event)
|
||||
static inline const char *
|
||||
dwc3_gadget_event_string(const struct dwc3_event_devt *event)
|
||||
{
|
||||
switch (event) {
|
||||
static char str[256];
|
||||
enum dwc3_link_state state = event->event_info & DWC3_LINK_STATE_MASK;
|
||||
|
||||
switch (event->type) {
|
||||
case DWC3_DEVICE_EVENT_DISCONNECT:
|
||||
return "Disconnect";
|
||||
sprintf(str, "Disconnect: [%s]",
|
||||
dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_RESET:
|
||||
return "Reset";
|
||||
sprintf(str, "Reset [%s]", dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_CONNECT_DONE:
|
||||
return "Connection Done";
|
||||
sprintf(str, "Connection Done [%s]",
|
||||
dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
|
||||
return "Link Status Change";
|
||||
sprintf(str, "Link Change [%s]",
|
||||
dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_WAKEUP:
|
||||
return "WakeUp";
|
||||
sprintf(str, "WakeUp [%s]", dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_EOPF:
|
||||
return "End-Of-Frame";
|
||||
sprintf(str, "End-Of-Frame [%s]",
|
||||
dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_SOF:
|
||||
return "Start-Of-Frame";
|
||||
sprintf(str, "Start-Of-Frame [%s]",
|
||||
dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
|
||||
return "Erratic Error";
|
||||
sprintf(str, "Erratic Error [%s]",
|
||||
dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_CMD_CMPL:
|
||||
return "Command Complete";
|
||||
sprintf(str, "Command Complete [%s]",
|
||||
dwc3_gadget_link_string(state));
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_OVERFLOW:
|
||||
return "Overflow";
|
||||
sprintf(str, "Overflow [%s]", dwc3_gadget_link_string(state));
|
||||
break;
|
||||
default:
|
||||
sprintf(str, "UNKNOWN");
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_ep_event_string - returns event name
|
||||
* @event: then event code
|
||||
*/
|
||||
static inline const char *dwc3_ep_event_string(u8 event)
|
||||
static inline const char *
|
||||
dwc3_ep_event_string(const struct dwc3_event_depevt *event)
|
||||
{
|
||||
switch (event) {
|
||||
u8 epnum = event->endpoint_number;
|
||||
static char str[256];
|
||||
int status;
|
||||
int ret;
|
||||
|
||||
ret = sprintf(str, "ep%d%s: ", epnum >> 1,
|
||||
(epnum & 1) ? "in" : "in");
|
||||
if (ret < 0)
|
||||
return "UNKNOWN";
|
||||
|
||||
switch (event->endpoint_event) {
|
||||
case DWC3_DEPEVT_XFERCOMPLETE:
|
||||
return "Transfer Complete";
|
||||
strcat(str, "Transfer Complete");
|
||||
break;
|
||||
case DWC3_DEPEVT_XFERINPROGRESS:
|
||||
return "Transfer In-Progress";
|
||||
strcat(str, "Transfer In-Progress");
|
||||
break;
|
||||
case DWC3_DEPEVT_XFERNOTREADY:
|
||||
return "Transfer Not Ready";
|
||||
strcat(str, "Transfer Not Ready");
|
||||
status = event->status & DEPEVT_STATUS_TRANSFER_ACTIVE;
|
||||
strcat(str, status ? " (Active)" : " (Not Active)");
|
||||
break;
|
||||
case DWC3_DEPEVT_RXTXFIFOEVT:
|
||||
return "FIFO";
|
||||
strcat(str, "FIFO");
|
||||
break;
|
||||
case DWC3_DEPEVT_STREAMEVT:
|
||||
return "Stream";
|
||||
status = event->status;
|
||||
|
||||
switch (status) {
|
||||
case DEPEVT_STREAMEVT_FOUND:
|
||||
sprintf(str + ret, " Stream %d Found",
|
||||
event->parameters);
|
||||
break;
|
||||
case DEPEVT_STREAMEVT_NOTFOUND:
|
||||
default:
|
||||
strcat(str, " Stream Not Found");
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
case DWC3_DEPEVT_EPCMDCMPLT:
|
||||
return "Endpoint Command Complete";
|
||||
strcat(str, "Endpoint Command Complete");
|
||||
break;
|
||||
default:
|
||||
sprintf(str, "UNKNOWN");
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,6 +270,46 @@ static inline const char *dwc3_gadget_event_type_string(u8 event)
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char *dwc3_decode_event(u32 event)
|
||||
{
|
||||
const union dwc3_event evt = (union dwc3_event) event;
|
||||
|
||||
if (evt.type.is_devspec)
|
||||
return dwc3_gadget_event_string(&evt.devt);
|
||||
else
|
||||
return dwc3_ep_event_string(&evt.depevt);
|
||||
}
|
||||
|
||||
static inline const char *dwc3_ep_cmd_status_string(int status)
|
||||
{
|
||||
switch (status) {
|
||||
case -ETIMEDOUT:
|
||||
return "Timed Out";
|
||||
case 0:
|
||||
return "Successful";
|
||||
case DEPEVT_TRANSFER_NO_RESOURCE:
|
||||
return "No Resource";
|
||||
case DEPEVT_TRANSFER_BUS_EXPIRY:
|
||||
return "Bus Expiry";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char *dwc3_gadget_generic_cmd_status_string(int status)
|
||||
{
|
||||
switch (status) {
|
||||
case -ETIMEDOUT:
|
||||
return "Timed Out";
|
||||
case 0:
|
||||
return "Successful";
|
||||
case 1:
|
||||
return "Error";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...);
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
+58
-133
@@ -36,9 +36,32 @@
|
||||
#define dump_register(nm) \
|
||||
{ \
|
||||
.name = __stringify(nm), \
|
||||
.offset = DWC3_ ##nm - DWC3_GLOBALS_REGS_START, \
|
||||
.offset = DWC3_ ##nm, \
|
||||
}
|
||||
|
||||
#define dump_ep_register_set(n) \
|
||||
{ \
|
||||
.name = "DEPCMDPAR2("__stringify(n)")", \
|
||||
.offset = DWC3_DEP_BASE(n) + \
|
||||
DWC3_DEPCMDPAR2, \
|
||||
}, \
|
||||
{ \
|
||||
.name = "DEPCMDPAR1("__stringify(n)")", \
|
||||
.offset = DWC3_DEP_BASE(n) + \
|
||||
DWC3_DEPCMDPAR1, \
|
||||
}, \
|
||||
{ \
|
||||
.name = "DEPCMDPAR0("__stringify(n)")", \
|
||||
.offset = DWC3_DEP_BASE(n) + \
|
||||
DWC3_DEPCMDPAR0, \
|
||||
}, \
|
||||
{ \
|
||||
.name = "DEPCMD("__stringify(n)")", \
|
||||
.offset = DWC3_DEP_BASE(n) + \
|
||||
DWC3_DEPCMD, \
|
||||
}
|
||||
|
||||
|
||||
static const struct debugfs_reg32 dwc3_regs[] = {
|
||||
dump_register(GSBUSCFG0),
|
||||
dump_register(GSBUSCFG1),
|
||||
@@ -47,6 +70,7 @@ static const struct debugfs_reg32 dwc3_regs[] = {
|
||||
dump_register(GCTL),
|
||||
dump_register(GEVTEN),
|
||||
dump_register(GSTS),
|
||||
dump_register(GUCTL1),
|
||||
dump_register(GSNPSID),
|
||||
dump_register(GGPIO),
|
||||
dump_register(GUID),
|
||||
@@ -218,137 +242,38 @@ static const struct debugfs_reg32 dwc3_regs[] = {
|
||||
dump_register(DGCMD),
|
||||
dump_register(DALEPENA),
|
||||
|
||||
dump_register(DEPCMDPAR2(0)),
|
||||
dump_register(DEPCMDPAR2(1)),
|
||||
dump_register(DEPCMDPAR2(2)),
|
||||
dump_register(DEPCMDPAR2(3)),
|
||||
dump_register(DEPCMDPAR2(4)),
|
||||
dump_register(DEPCMDPAR2(5)),
|
||||
dump_register(DEPCMDPAR2(6)),
|
||||
dump_register(DEPCMDPAR2(7)),
|
||||
dump_register(DEPCMDPAR2(8)),
|
||||
dump_register(DEPCMDPAR2(9)),
|
||||
dump_register(DEPCMDPAR2(10)),
|
||||
dump_register(DEPCMDPAR2(11)),
|
||||
dump_register(DEPCMDPAR2(12)),
|
||||
dump_register(DEPCMDPAR2(13)),
|
||||
dump_register(DEPCMDPAR2(14)),
|
||||
dump_register(DEPCMDPAR2(15)),
|
||||
dump_register(DEPCMDPAR2(16)),
|
||||
dump_register(DEPCMDPAR2(17)),
|
||||
dump_register(DEPCMDPAR2(18)),
|
||||
dump_register(DEPCMDPAR2(19)),
|
||||
dump_register(DEPCMDPAR2(20)),
|
||||
dump_register(DEPCMDPAR2(21)),
|
||||
dump_register(DEPCMDPAR2(22)),
|
||||
dump_register(DEPCMDPAR2(23)),
|
||||
dump_register(DEPCMDPAR2(24)),
|
||||
dump_register(DEPCMDPAR2(25)),
|
||||
dump_register(DEPCMDPAR2(26)),
|
||||
dump_register(DEPCMDPAR2(27)),
|
||||
dump_register(DEPCMDPAR2(28)),
|
||||
dump_register(DEPCMDPAR2(29)),
|
||||
dump_register(DEPCMDPAR2(30)),
|
||||
dump_register(DEPCMDPAR2(31)),
|
||||
|
||||
dump_register(DEPCMDPAR1(0)),
|
||||
dump_register(DEPCMDPAR1(1)),
|
||||
dump_register(DEPCMDPAR1(2)),
|
||||
dump_register(DEPCMDPAR1(3)),
|
||||
dump_register(DEPCMDPAR1(4)),
|
||||
dump_register(DEPCMDPAR1(5)),
|
||||
dump_register(DEPCMDPAR1(6)),
|
||||
dump_register(DEPCMDPAR1(7)),
|
||||
dump_register(DEPCMDPAR1(8)),
|
||||
dump_register(DEPCMDPAR1(9)),
|
||||
dump_register(DEPCMDPAR1(10)),
|
||||
dump_register(DEPCMDPAR1(11)),
|
||||
dump_register(DEPCMDPAR1(12)),
|
||||
dump_register(DEPCMDPAR1(13)),
|
||||
dump_register(DEPCMDPAR1(14)),
|
||||
dump_register(DEPCMDPAR1(15)),
|
||||
dump_register(DEPCMDPAR1(16)),
|
||||
dump_register(DEPCMDPAR1(17)),
|
||||
dump_register(DEPCMDPAR1(18)),
|
||||
dump_register(DEPCMDPAR1(19)),
|
||||
dump_register(DEPCMDPAR1(20)),
|
||||
dump_register(DEPCMDPAR1(21)),
|
||||
dump_register(DEPCMDPAR1(22)),
|
||||
dump_register(DEPCMDPAR1(23)),
|
||||
dump_register(DEPCMDPAR1(24)),
|
||||
dump_register(DEPCMDPAR1(25)),
|
||||
dump_register(DEPCMDPAR1(26)),
|
||||
dump_register(DEPCMDPAR1(27)),
|
||||
dump_register(DEPCMDPAR1(28)),
|
||||
dump_register(DEPCMDPAR1(29)),
|
||||
dump_register(DEPCMDPAR1(30)),
|
||||
dump_register(DEPCMDPAR1(31)),
|
||||
|
||||
dump_register(DEPCMDPAR0(0)),
|
||||
dump_register(DEPCMDPAR0(1)),
|
||||
dump_register(DEPCMDPAR0(2)),
|
||||
dump_register(DEPCMDPAR0(3)),
|
||||
dump_register(DEPCMDPAR0(4)),
|
||||
dump_register(DEPCMDPAR0(5)),
|
||||
dump_register(DEPCMDPAR0(6)),
|
||||
dump_register(DEPCMDPAR0(7)),
|
||||
dump_register(DEPCMDPAR0(8)),
|
||||
dump_register(DEPCMDPAR0(9)),
|
||||
dump_register(DEPCMDPAR0(10)),
|
||||
dump_register(DEPCMDPAR0(11)),
|
||||
dump_register(DEPCMDPAR0(12)),
|
||||
dump_register(DEPCMDPAR0(13)),
|
||||
dump_register(DEPCMDPAR0(14)),
|
||||
dump_register(DEPCMDPAR0(15)),
|
||||
dump_register(DEPCMDPAR0(16)),
|
||||
dump_register(DEPCMDPAR0(17)),
|
||||
dump_register(DEPCMDPAR0(18)),
|
||||
dump_register(DEPCMDPAR0(19)),
|
||||
dump_register(DEPCMDPAR0(20)),
|
||||
dump_register(DEPCMDPAR0(21)),
|
||||
dump_register(DEPCMDPAR0(22)),
|
||||
dump_register(DEPCMDPAR0(23)),
|
||||
dump_register(DEPCMDPAR0(24)),
|
||||
dump_register(DEPCMDPAR0(25)),
|
||||
dump_register(DEPCMDPAR0(26)),
|
||||
dump_register(DEPCMDPAR0(27)),
|
||||
dump_register(DEPCMDPAR0(28)),
|
||||
dump_register(DEPCMDPAR0(29)),
|
||||
dump_register(DEPCMDPAR0(30)),
|
||||
dump_register(DEPCMDPAR0(31)),
|
||||
|
||||
dump_register(DEPCMD(0)),
|
||||
dump_register(DEPCMD(1)),
|
||||
dump_register(DEPCMD(2)),
|
||||
dump_register(DEPCMD(3)),
|
||||
dump_register(DEPCMD(4)),
|
||||
dump_register(DEPCMD(5)),
|
||||
dump_register(DEPCMD(6)),
|
||||
dump_register(DEPCMD(7)),
|
||||
dump_register(DEPCMD(8)),
|
||||
dump_register(DEPCMD(9)),
|
||||
dump_register(DEPCMD(10)),
|
||||
dump_register(DEPCMD(11)),
|
||||
dump_register(DEPCMD(12)),
|
||||
dump_register(DEPCMD(13)),
|
||||
dump_register(DEPCMD(14)),
|
||||
dump_register(DEPCMD(15)),
|
||||
dump_register(DEPCMD(16)),
|
||||
dump_register(DEPCMD(17)),
|
||||
dump_register(DEPCMD(18)),
|
||||
dump_register(DEPCMD(19)),
|
||||
dump_register(DEPCMD(20)),
|
||||
dump_register(DEPCMD(21)),
|
||||
dump_register(DEPCMD(22)),
|
||||
dump_register(DEPCMD(23)),
|
||||
dump_register(DEPCMD(24)),
|
||||
dump_register(DEPCMD(25)),
|
||||
dump_register(DEPCMD(26)),
|
||||
dump_register(DEPCMD(27)),
|
||||
dump_register(DEPCMD(28)),
|
||||
dump_register(DEPCMD(29)),
|
||||
dump_register(DEPCMD(30)),
|
||||
dump_register(DEPCMD(31)),
|
||||
dump_ep_register_set(0),
|
||||
dump_ep_register_set(1),
|
||||
dump_ep_register_set(2),
|
||||
dump_ep_register_set(3),
|
||||
dump_ep_register_set(4),
|
||||
dump_ep_register_set(5),
|
||||
dump_ep_register_set(6),
|
||||
dump_ep_register_set(7),
|
||||
dump_ep_register_set(8),
|
||||
dump_ep_register_set(9),
|
||||
dump_ep_register_set(10),
|
||||
dump_ep_register_set(11),
|
||||
dump_ep_register_set(12),
|
||||
dump_ep_register_set(13),
|
||||
dump_ep_register_set(14),
|
||||
dump_ep_register_set(15),
|
||||
dump_ep_register_set(16),
|
||||
dump_ep_register_set(17),
|
||||
dump_ep_register_set(18),
|
||||
dump_ep_register_set(19),
|
||||
dump_ep_register_set(20),
|
||||
dump_ep_register_set(21),
|
||||
dump_ep_register_set(22),
|
||||
dump_ep_register_set(23),
|
||||
dump_ep_register_set(24),
|
||||
dump_ep_register_set(25),
|
||||
dump_ep_register_set(26),
|
||||
dump_ep_register_set(27),
|
||||
dump_ep_register_set(28),
|
||||
dump_ep_register_set(29),
|
||||
dump_ep_register_set(30),
|
||||
dump_ep_register_set(31),
|
||||
|
||||
dump_register(OCFG),
|
||||
dump_register(OCTL),
|
||||
@@ -939,7 +864,7 @@ void dwc3_debugfs_init(struct dwc3 *dwc)
|
||||
|
||||
dwc->regset->regs = dwc3_regs;
|
||||
dwc->regset->nregs = ARRAY_SIZE(dwc3_regs);
|
||||
dwc->regset->base = dwc->regs;
|
||||
dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START;
|
||||
|
||||
file = debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset);
|
||||
if (!file)
|
||||
|
||||
@@ -165,7 +165,7 @@ static void dwc3_omap_write_utmi_ctrl(struct dwc3_omap *omap, u32 value)
|
||||
|
||||
static u32 dwc3_omap_read_irq0_status(struct dwc3_omap *omap)
|
||||
{
|
||||
return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_0 -
|
||||
return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_RAW_0 -
|
||||
omap->irq0_offset);
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ static void dwc3_omap_write_irq0_status(struct dwc3_omap *omap, u32 value)
|
||||
|
||||
static u32 dwc3_omap_read_irqmisc_status(struct dwc3_omap *omap)
|
||||
{
|
||||
return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_MISC +
|
||||
return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_RAW_MISC +
|
||||
omap->irqmisc_offset);
|
||||
}
|
||||
|
||||
@@ -231,35 +231,30 @@ static void dwc3_omap_set_mailbox(struct dwc3_omap *omap,
|
||||
}
|
||||
|
||||
val = dwc3_omap_read_utmi_ctrl(omap);
|
||||
val &= ~(USBOTGSS_UTMI_OTG_CTRL_IDDIG
|
||||
| USBOTGSS_UTMI_OTG_CTRL_VBUSVALID
|
||||
| USBOTGSS_UTMI_OTG_CTRL_SESSEND);
|
||||
val |= USBOTGSS_UTMI_OTG_CTRL_SESSVALID
|
||||
| USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT;
|
||||
val &= ~USBOTGSS_UTMI_OTG_CTRL_IDDIG;
|
||||
dwc3_omap_write_utmi_ctrl(omap, val);
|
||||
break;
|
||||
|
||||
case OMAP_DWC3_VBUS_VALID:
|
||||
val = dwc3_omap_read_utmi_ctrl(omap);
|
||||
val &= ~USBOTGSS_UTMI_OTG_CTRL_SESSEND;
|
||||
val |= USBOTGSS_UTMI_OTG_CTRL_IDDIG
|
||||
| USBOTGSS_UTMI_OTG_CTRL_VBUSVALID
|
||||
| USBOTGSS_UTMI_OTG_CTRL_SESSVALID
|
||||
| USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT;
|
||||
val |= USBOTGSS_UTMI_OTG_CTRL_VBUSVALID
|
||||
| USBOTGSS_UTMI_OTG_CTRL_SESSVALID;
|
||||
dwc3_omap_write_utmi_ctrl(omap, val);
|
||||
break;
|
||||
|
||||
case OMAP_DWC3_ID_FLOAT:
|
||||
if (omap->vbus_reg)
|
||||
regulator_disable(omap->vbus_reg);
|
||||
val = dwc3_omap_read_utmi_ctrl(omap);
|
||||
val |= USBOTGSS_UTMI_OTG_CTRL_IDDIG;
|
||||
dwc3_omap_write_utmi_ctrl(omap, val);
|
||||
|
||||
case OMAP_DWC3_VBUS_OFF:
|
||||
val = dwc3_omap_read_utmi_ctrl(omap);
|
||||
val &= ~(USBOTGSS_UTMI_OTG_CTRL_SESSVALID
|
||||
| USBOTGSS_UTMI_OTG_CTRL_VBUSVALID
|
||||
| USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT);
|
||||
val |= USBOTGSS_UTMI_OTG_CTRL_SESSEND
|
||||
| USBOTGSS_UTMI_OTG_CTRL_IDDIG;
|
||||
| USBOTGSS_UTMI_OTG_CTRL_VBUSVALID);
|
||||
val |= USBOTGSS_UTMI_OTG_CTRL_SESSEND;
|
||||
dwc3_omap_write_utmi_ctrl(omap, val);
|
||||
break;
|
||||
|
||||
@@ -268,19 +263,38 @@ static void dwc3_omap_set_mailbox(struct dwc3_omap *omap,
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_omap_enable_irqs(struct dwc3_omap *omap);
|
||||
static void dwc3_omap_disable_irqs(struct dwc3_omap *omap);
|
||||
|
||||
static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
|
||||
{
|
||||
struct dwc3_omap *omap = _omap;
|
||||
|
||||
if (dwc3_omap_read_irqmisc_status(omap) ||
|
||||
dwc3_omap_read_irq0_status(omap)) {
|
||||
/* mask irqs */
|
||||
dwc3_omap_disable_irqs(omap);
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_omap_interrupt_thread(int irq, void *_omap)
|
||||
{
|
||||
struct dwc3_omap *omap = _omap;
|
||||
u32 reg;
|
||||
|
||||
/* clear irq status flags */
|
||||
reg = dwc3_omap_read_irqmisc_status(omap);
|
||||
|
||||
dwc3_omap_write_irqmisc_status(omap, reg);
|
||||
|
||||
reg = dwc3_omap_read_irq0_status(omap);
|
||||
|
||||
dwc3_omap_write_irq0_status(omap, reg);
|
||||
|
||||
/* unmask irqs */
|
||||
dwc3_omap_enable_irqs(omap);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@@ -497,8 +511,9 @@ static int dwc3_omap_probe(struct platform_device *pdev)
|
||||
/* check the DMA Status */
|
||||
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
|
||||
|
||||
ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
|
||||
"dwc3-omap", omap);
|
||||
ret = devm_request_threaded_irq(dev, omap->irq, dwc3_omap_interrupt,
|
||||
dwc3_omap_interrupt_thread, IRQF_SHARED,
|
||||
"dwc3-omap", omap);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request IRQ #%d --> %d\n",
|
||||
omap->irq, ret);
|
||||
|
||||
+101
-56
@@ -20,11 +20,11 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#include "platform_data.h"
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd
|
||||
#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI 0xabce
|
||||
@@ -51,62 +51,70 @@ static int dwc3_pci_quirks(struct pci_dev *pdev, struct platform_device *dwc3)
|
||||
{
|
||||
if (pdev->vendor == PCI_VENDOR_ID_AMD &&
|
||||
pdev->device == PCI_DEVICE_ID_AMD_NL_USB) {
|
||||
struct dwc3_platform_data pdata;
|
||||
struct property_entry properties[] = {
|
||||
PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"),
|
||||
PROPERTY_ENTRY_U8("snps,lpm-nyet-threshold", 0xf),
|
||||
PROPERTY_ENTRY_BOOL("snps,u2exit_lfps_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,u2ss_inp3_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,req_p1p2p3_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,del_p1p2p3_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,del_phy_power_chg_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,lfps_filter_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,rx_detect_poll_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,tx_de_emphasis_quirk"),
|
||||
PROPERTY_ENTRY_U8("snps,tx_de_emphasis", 1),
|
||||
/*
|
||||
* FIXME these quirks should be removed when AMD NL
|
||||
* tapes out
|
||||
*/
|
||||
PROPERTY_ENTRY_BOOL("snps,disable_scramble_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,dis_u3_susphy_quirk"),
|
||||
PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"),
|
||||
{ },
|
||||
};
|
||||
|
||||
memset(&pdata, 0, sizeof(pdata));
|
||||
|
||||
pdata.has_lpm_erratum = true;
|
||||
pdata.lpm_nyet_threshold = 0xf;
|
||||
|
||||
pdata.u2exit_lfps_quirk = true;
|
||||
pdata.u2ss_inp3_quirk = true;
|
||||
pdata.req_p1p2p3_quirk = true;
|
||||
pdata.del_p1p2p3_quirk = true;
|
||||
pdata.del_phy_power_chg_quirk = true;
|
||||
pdata.lfps_filter_quirk = true;
|
||||
pdata.rx_detect_poll_quirk = true;
|
||||
|
||||
pdata.tx_de_emphasis_quirk = true;
|
||||
pdata.tx_de_emphasis = 1;
|
||||
|
||||
/*
|
||||
* FIXME these quirks should be removed when AMD NL
|
||||
* taps out
|
||||
*/
|
||||
pdata.disable_scramble_quirk = true;
|
||||
pdata.dis_u3_susphy_quirk = true;
|
||||
pdata.dis_u2_susphy_quirk = true;
|
||||
|
||||
return platform_device_add_data(dwc3, &pdata, sizeof(pdata));
|
||||
return platform_device_add_properties(dwc3, properties);
|
||||
}
|
||||
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
|
||||
pdev->device == PCI_DEVICE_ID_INTEL_BYT) {
|
||||
struct gpio_desc *gpio;
|
||||
if (pdev->vendor == PCI_VENDOR_ID_INTEL) {
|
||||
int ret;
|
||||
|
||||
acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev),
|
||||
acpi_dwc3_byt_gpios);
|
||||
struct property_entry properties[] = {
|
||||
PROPERTY_ENTRY_STRING("dr-mode", "peripheral"),
|
||||
{ }
|
||||
};
|
||||
|
||||
/*
|
||||
* These GPIOs will turn on the USB2 PHY. Note that we have to
|
||||
* put the gpio descriptors again here because the phy driver
|
||||
* might want to grab them, too.
|
||||
*/
|
||||
gpio = gpiod_get_optional(&pdev->dev, "cs", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
ret = platform_device_add_properties(dwc3, properties);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
gpiod_set_value_cansleep(gpio, 1);
|
||||
gpiod_put(gpio);
|
||||
if (pdev->device == PCI_DEVICE_ID_INTEL_BYT) {
|
||||
struct gpio_desc *gpio;
|
||||
|
||||
gpio = gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev),
|
||||
acpi_dwc3_byt_gpios);
|
||||
|
||||
/*
|
||||
* These GPIOs will turn on the USB2 PHY. Note that we have to
|
||||
* put the gpio descriptors again here because the phy driver
|
||||
* might want to grab them, too.
|
||||
*/
|
||||
gpio = gpiod_get_optional(&pdev->dev, "cs", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
||||
if (gpio) {
|
||||
gpiod_set_value_cansleep(gpio, 1);
|
||||
gpiod_put(gpio);
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
gpio = gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(gpio))
|
||||
return PTR_ERR(gpio);
|
||||
|
||||
if (gpio) {
|
||||
gpiod_set_value_cansleep(gpio, 1);
|
||||
gpiod_put(gpio);
|
||||
usleep_range(10000, 11000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,15 +122,14 @@ static int dwc3_pci_quirks(struct pci_dev *pdev, struct platform_device *dwc3)
|
||||
(pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 ||
|
||||
pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI ||
|
||||
pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31)) {
|
||||
struct property_entry properties[] = {
|
||||
PROPERTY_ENTRY_BOOL("snps,usb3_lpm_capable"),
|
||||
PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"),
|
||||
PROPERTY_ENTRY_BOOL("snps,dis_enblslpm_quirk"),
|
||||
{ },
|
||||
};
|
||||
|
||||
struct dwc3_platform_data pdata;
|
||||
|
||||
memset(&pdata, 0, sizeof(pdata));
|
||||
pdata.usb3_lpm_capable = true;
|
||||
pdata.has_lpm_erratum = true;
|
||||
pdata.dis_enblslpm_quirk = true;
|
||||
|
||||
return platform_device_add_data(dwc3, &pdata, sizeof(pdata));
|
||||
return platform_device_add_properties(dwc3, properties);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -180,7 +187,11 @@ static int dwc3_pci_probe(struct pci_dev *pci,
|
||||
goto err;
|
||||
}
|
||||
|
||||
device_init_wakeup(dev, true);
|
||||
device_set_run_wake(dev, true);
|
||||
pci_set_drvdata(pci, dwc3);
|
||||
pm_runtime_put(dev);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
platform_device_put(dwc3);
|
||||
@@ -189,6 +200,8 @@ err:
|
||||
|
||||
static void dwc3_pci_remove(struct pci_dev *pci)
|
||||
{
|
||||
device_init_wakeup(&pci->dev, false);
|
||||
pm_runtime_get(&pci->dev);
|
||||
acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pci->dev));
|
||||
platform_device_unregister(pci_get_drvdata(pci));
|
||||
}
|
||||
@@ -219,11 +232,43 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int dwc3_pci_runtime_suspend(struct device *dev)
|
||||
{
|
||||
if (device_run_wake(dev))
|
||||
return 0;
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static int dwc3_pci_pm_dummy(struct device *dev)
|
||||
{
|
||||
/*
|
||||
* There's nothing to do here. No, seriously. Everything is either taken
|
||||
* care either by PCI subsystem or dwc3/core.c, so we have nothing
|
||||
* missing here.
|
||||
*
|
||||
* So you'd think we didn't need this at all, but PCI subsystem will
|
||||
* bail out if we don't have a valid callback :-s
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static struct dev_pm_ops dwc3_pci_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(dwc3_pci_pm_dummy, dwc3_pci_pm_dummy)
|
||||
SET_RUNTIME_PM_OPS(dwc3_pci_runtime_suspend, dwc3_pci_pm_dummy,
|
||||
NULL)
|
||||
};
|
||||
|
||||
static struct pci_driver dwc3_pci_driver = {
|
||||
.name = "dwc3-pci",
|
||||
.id_table = dwc3_pci_id_table,
|
||||
.probe = dwc3_pci_probe,
|
||||
.remove = dwc3_pci_remove,
|
||||
.driver = {
|
||||
.pm = &dwc3_pci_dev_pm_ops,
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
|
||||
|
||||
+10
-16
@@ -98,8 +98,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
|
||||
|
||||
trace_dwc3_prepare_trb(dep, trb);
|
||||
|
||||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
|
||||
DWC3_DEPCMD_STARTTRANSFER, ¶ms);
|
||||
ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_STARTTRANSFER, ¶ms);
|
||||
if (ret < 0) {
|
||||
dwc3_trace(trace_dwc3_ep0, "%s STARTTRANSFER failed",
|
||||
dep->name);
|
||||
@@ -107,9 +106,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
|
||||
}
|
||||
|
||||
dep->flags |= DWC3_EP_BUSY;
|
||||
dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc,
|
||||
dep->number);
|
||||
|
||||
dep->resource_index = dwc3_gadget_ep_get_transfer_index(dep);
|
||||
dwc->ep0_next_event = DWC3_EP0_COMPLETE;
|
||||
|
||||
return 0;
|
||||
@@ -499,7 +496,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
|
||||
case USB_RECIP_ENDPOINT:
|
||||
switch (wValue) {
|
||||
case USB_ENDPOINT_HALT:
|
||||
dep = dwc3_wIndex_to_dep(dwc, wIndex);
|
||||
dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex);
|
||||
if (!dep)
|
||||
return -EINVAL;
|
||||
if (set == 0 && (dep->flags & DWC3_EP_WEDGE))
|
||||
@@ -622,8 +619,8 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
|
||||
struct timing {
|
||||
u8 u1sel;
|
||||
u8 u1pel;
|
||||
u16 u2sel;
|
||||
u16 u2pel;
|
||||
__le16 u2sel;
|
||||
__le16 u2pel;
|
||||
} __packed timing;
|
||||
|
||||
int ret;
|
||||
@@ -980,7 +977,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
||||
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
|
||||
dep->number);
|
||||
if (ret) {
|
||||
dwc3_trace(trace_dwc3_ep0, "failed to map request\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "failed to map request");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1008,7 +1005,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
||||
ret = usb_gadget_map_request(&dwc->gadget, &req->request,
|
||||
dep->number);
|
||||
if (ret) {
|
||||
dwc3_trace(trace_dwc3_ep0, "failed to map request\n");
|
||||
dwc3_trace(trace_dwc3_ep0, "failed to map request");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1058,7 +1055,7 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
cmd |= DWC3_DEPCMD_CMDIOC;
|
||||
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms);
|
||||
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
|
||||
WARN_ON_ONCE(ret);
|
||||
dep->resource_index = 0;
|
||||
}
|
||||
@@ -1112,11 +1109,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
|
||||
void dwc3_ep0_interrupt(struct dwc3 *dwc,
|
||||
const struct dwc3_event_depevt *event)
|
||||
{
|
||||
u8 epnum = event->endpoint_number;
|
||||
|
||||
dwc3_trace(trace_dwc3_ep0, "%s while ep%d%s in state '%s'",
|
||||
dwc3_ep_event_string(event->endpoint_event),
|
||||
epnum >> 1, (epnum & 1) ? "in" : "out",
|
||||
dwc3_trace(trace_dwc3_ep0, "%s: state '%s'",
|
||||
dwc3_ep_event_string(event),
|
||||
dwc3_ep0_state_string(dwc->ep0state));
|
||||
|
||||
switch (event->endpoint_event) {
|
||||
|
||||
+504
-373
File diff suppressed because it is too large
Load Diff
@@ -95,11 +95,11 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
|
||||
*
|
||||
* Caller should take care of locking
|
||||
*/
|
||||
static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3 *dwc, u8 number)
|
||||
static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep)
|
||||
{
|
||||
u32 res_id;
|
||||
|
||||
res_id = dwc3_readl(dwc->regs, DWC3_DEPCMD(number));
|
||||
res_id = dwc3_readl(dep->regs, DWC3_DEPCMD);
|
||||
|
||||
return DWC3_DEPCMD_GET_RSC_IDX(res_id);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
|
||||
static inline u32 dwc3_readl(void __iomem *base, u32 offset)
|
||||
{
|
||||
u32 offs = offset - DWC3_GLOBALS_REGS_START;
|
||||
u32 value;
|
||||
|
||||
/*
|
||||
@@ -34,7 +33,7 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset)
|
||||
* space, see dwc3_probe in core.c.
|
||||
* However, the offsets are given starting from xHCI address space.
|
||||
*/
|
||||
value = readl(base + offs);
|
||||
value = readl(base + offset - DWC3_GLOBALS_REGS_START);
|
||||
|
||||
/*
|
||||
* When tracing we want to make it easy to find the correct address on
|
||||
@@ -49,14 +48,12 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset)
|
||||
|
||||
static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
|
||||
{
|
||||
u32 offs = offset - DWC3_GLOBALS_REGS_START;
|
||||
|
||||
/*
|
||||
* We requested the mem region starting from the Globals address
|
||||
* space, see dwc3_probe in core.c.
|
||||
* However, the offsets are given starting from xHCI address space.
|
||||
*/
|
||||
writel(value, base + offs);
|
||||
writel(value, base + offset - DWC3_GLOBALS_REGS_START);
|
||||
|
||||
/*
|
||||
* When tracing we want to make it easy to find the correct address on
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
/**
|
||||
* platform_data.h - USB DWC3 Platform Data Support
|
||||
*
|
||||
* Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
|
||||
* Author: Felipe Balbi <balbi@ti.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 of
|
||||
* the License as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/usb/ch9.h>
|
||||
#include <linux/usb/otg.h>
|
||||
|
||||
struct dwc3_platform_data {
|
||||
enum usb_device_speed maximum_speed;
|
||||
enum usb_dr_mode dr_mode;
|
||||
bool usb3_lpm_capable;
|
||||
|
||||
unsigned is_utmi_l1_suspend:1;
|
||||
u8 hird_threshold;
|
||||
|
||||
u8 lpm_nyet_threshold;
|
||||
|
||||
unsigned disable_scramble_quirk:1;
|
||||
unsigned has_lpm_erratum:1;
|
||||
unsigned u2exit_lfps_quirk:1;
|
||||
unsigned u2ss_inp3_quirk:1;
|
||||
unsigned req_p1p2p3_quirk:1;
|
||||
unsigned del_p1p2p3_quirk:1;
|
||||
unsigned del_phy_power_chg_quirk:1;
|
||||
unsigned lfps_filter_quirk:1;
|
||||
unsigned rx_detect_poll_quirk:1;
|
||||
unsigned dis_u3_susphy_quirk:1;
|
||||
unsigned dis_u2_susphy_quirk:1;
|
||||
unsigned dis_enblslpm_quirk:1;
|
||||
unsigned dis_rxdet_inp3_quirk:1;
|
||||
|
||||
unsigned tx_de_emphasis_quirk:1;
|
||||
unsigned tx_de_emphasis:2;
|
||||
|
||||
u32 fladj_value;
|
||||
|
||||
const char *hsphy_interface;
|
||||
};
|
||||
+72
-24
@@ -71,7 +71,8 @@ DECLARE_EVENT_CLASS(dwc3_log_event,
|
||||
TP_fast_assign(
|
||||
__entry->event = event;
|
||||
),
|
||||
TP_printk("event %08x", __entry->event)
|
||||
TP_printk("event (%08x): %s", __entry->event,
|
||||
dwc3_decode_event(__entry->event))
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_event, dwc3_event,
|
||||
@@ -85,21 +86,21 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl,
|
||||
TP_STRUCT__entry(
|
||||
__field(__u8, bRequestType)
|
||||
__field(__u8, bRequest)
|
||||
__field(__le16, wValue)
|
||||
__field(__le16, wIndex)
|
||||
__field(__le16, wLength)
|
||||
__field(__u16, wValue)
|
||||
__field(__u16, wIndex)
|
||||
__field(__u16, wLength)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->bRequestType = ctrl->bRequestType;
|
||||
__entry->bRequest = ctrl->bRequest;
|
||||
__entry->wValue = ctrl->wValue;
|
||||
__entry->wIndex = ctrl->wIndex;
|
||||
__entry->wLength = ctrl->wLength;
|
||||
__entry->wValue = le16_to_cpu(ctrl->wValue);
|
||||
__entry->wIndex = le16_to_cpu(ctrl->wIndex);
|
||||
__entry->wLength = le16_to_cpu(ctrl->wLength);
|
||||
),
|
||||
TP_printk("bRequestType %02x bRequest %02x wValue %04x wIndex %04x wLength %d",
|
||||
__entry->bRequestType, __entry->bRequest,
|
||||
le16_to_cpu(__entry->wValue), le16_to_cpu(__entry->wIndex),
|
||||
le16_to_cpu(__entry->wLength)
|
||||
__entry->wValue, __entry->wIndex,
|
||||
__entry->wLength
|
||||
)
|
||||
);
|
||||
|
||||
@@ -166,37 +167,41 @@ DEFINE_EVENT(dwc3_log_request, dwc3_gadget_giveback,
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_generic_cmd,
|
||||
TP_PROTO(unsigned int cmd, u32 param),
|
||||
TP_ARGS(cmd, param),
|
||||
TP_PROTO(unsigned int cmd, u32 param, int status),
|
||||
TP_ARGS(cmd, param, status),
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, cmd)
|
||||
__field(u32, param)
|
||||
__field(int, status)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->cmd = cmd;
|
||||
__entry->param = param;
|
||||
__entry->status = status;
|
||||
),
|
||||
TP_printk("cmd '%s' [%d] param %08x",
|
||||
TP_printk("cmd '%s' [%d] param %08x --> status: %s",
|
||||
dwc3_gadget_generic_cmd_string(__entry->cmd),
|
||||
__entry->cmd, __entry->param
|
||||
__entry->cmd, __entry->param,
|
||||
dwc3_gadget_generic_cmd_status_string(__entry->status)
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_generic_cmd, dwc3_gadget_generic_cmd,
|
||||
TP_PROTO(unsigned int cmd, u32 param),
|
||||
TP_ARGS(cmd, param)
|
||||
TP_PROTO(unsigned int cmd, u32 param, int status),
|
||||
TP_ARGS(cmd, param, status)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
|
||||
TP_PROTO(struct dwc3_ep *dep, unsigned int cmd,
|
||||
struct dwc3_gadget_ep_cmd_params *params),
|
||||
TP_ARGS(dep, cmd, params),
|
||||
struct dwc3_gadget_ep_cmd_params *params, int cmd_status),
|
||||
TP_ARGS(dep, cmd, params, cmd_status),
|
||||
TP_STRUCT__entry(
|
||||
__dynamic_array(char, name, DWC3_MSG_MAX)
|
||||
__field(unsigned int, cmd)
|
||||
__field(u32, param0)
|
||||
__field(u32, param1)
|
||||
__field(u32, param2)
|
||||
__field(int, cmd_status)
|
||||
),
|
||||
TP_fast_assign(
|
||||
snprintf(__get_str(name), DWC3_MSG_MAX, "%s", dep->name);
|
||||
@@ -204,18 +209,20 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
|
||||
__entry->param0 = params->param0;
|
||||
__entry->param1 = params->param1;
|
||||
__entry->param2 = params->param2;
|
||||
__entry->cmd_status = cmd_status;
|
||||
),
|
||||
TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x",
|
||||
TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x --> status: %s",
|
||||
__get_str(name), dwc3_gadget_ep_cmd_string(__entry->cmd),
|
||||
__entry->cmd, __entry->param0,
|
||||
__entry->param1, __entry->param2
|
||||
__entry->param1, __entry->param2,
|
||||
dwc3_ep_cmd_status_string(__entry->cmd_status)
|
||||
)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(dwc3_log_gadget_ep_cmd, dwc3_gadget_ep_cmd,
|
||||
TP_PROTO(struct dwc3_ep *dep, unsigned int cmd,
|
||||
struct dwc3_gadget_ep_cmd_params *params),
|
||||
TP_ARGS(dep, cmd, params)
|
||||
struct dwc3_gadget_ep_cmd_params *params, int cmd_status),
|
||||
TP_ARGS(dep, cmd, params, cmd_status)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(dwc3_log_trb,
|
||||
@@ -224,6 +231,8 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
|
||||
TP_STRUCT__entry(
|
||||
__dynamic_array(char, name, DWC3_MSG_MAX)
|
||||
__field(struct dwc3_trb *, trb)
|
||||
__field(u32, allocated)
|
||||
__field(u32, queued)
|
||||
__field(u32, bpl)
|
||||
__field(u32, bph)
|
||||
__field(u32, size)
|
||||
@@ -232,14 +241,53 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
|
||||
TP_fast_assign(
|
||||
snprintf(__get_str(name), DWC3_MSG_MAX, "%s", dep->name);
|
||||
__entry->trb = trb;
|
||||
__entry->allocated = dep->allocated_requests;
|
||||
__entry->queued = dep->queued_requests;
|
||||
__entry->bpl = trb->bpl;
|
||||
__entry->bph = trb->bph;
|
||||
__entry->size = trb->size;
|
||||
__entry->ctrl = trb->ctrl;
|
||||
),
|
||||
TP_printk("%s: trb %p bph %08x bpl %08x size %08x ctrl %08x",
|
||||
__get_str(name), __entry->trb, __entry->bph, __entry->bpl,
|
||||
__entry->size, __entry->ctrl
|
||||
TP_printk("%s: %d/%d trb %p buf %08x%08x size %d ctrl %08x (%c%c%c%c:%c%c:%s)",
|
||||
__get_str(name), __entry->queued, __entry->allocated,
|
||||
__entry->trb, __entry->bph, __entry->bpl,
|
||||
__entry->size, __entry->ctrl,
|
||||
__entry->ctrl & DWC3_TRB_CTRL_HWO ? 'H' : 'h',
|
||||
__entry->ctrl & DWC3_TRB_CTRL_LST ? 'L' : 'l',
|
||||
__entry->ctrl & DWC3_TRB_CTRL_CHN ? 'C' : 'c',
|
||||
__entry->ctrl & DWC3_TRB_CTRL_CSP ? 'S' : 's',
|
||||
__entry->ctrl & DWC3_TRB_CTRL_ISP_IMI ? 'S' : 's',
|
||||
__entry->ctrl & DWC3_TRB_CTRL_IOC ? 'C' : 'c',
|
||||
({char *s;
|
||||
switch (__entry->ctrl & 0x3f0) {
|
||||
case DWC3_TRBCTL_NORMAL:
|
||||
s = "normal";
|
||||
break;
|
||||
case DWC3_TRBCTL_CONTROL_SETUP:
|
||||
s = "setup";
|
||||
break;
|
||||
case DWC3_TRBCTL_CONTROL_STATUS2:
|
||||
s = "status2";
|
||||
break;
|
||||
case DWC3_TRBCTL_CONTROL_STATUS3:
|
||||
s = "status3";
|
||||
break;
|
||||
case DWC3_TRBCTL_CONTROL_DATA:
|
||||
s = "data";
|
||||
break;
|
||||
case DWC3_TRBCTL_ISOCHRONOUS_FIRST:
|
||||
s = "isoc-first";
|
||||
break;
|
||||
case DWC3_TRBCTL_ISOCHRONOUS:
|
||||
s = "isoc";
|
||||
break;
|
||||
case DWC3_TRBCTL_LINK_TRB:
|
||||
s = "link";
|
||||
break;
|
||||
default:
|
||||
s = "UNKNOWN";
|
||||
break;
|
||||
} s; })
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user