mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-08 21:22:09 -04:00
Merge "usb: dwc3: Add QTI MSM platform specific feature and other changes"
This commit is contained in:
commit
4c196dd091
8 changed files with 735 additions and 153 deletions
|
|
@ -31,6 +31,7 @@
|
|||
#include <linux/usb/gadget.h>
|
||||
#include <linux/usb/of.h>
|
||||
#include <linux/usb/otg.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "gadget.h"
|
||||
|
|
@ -38,11 +39,13 @@
|
|||
|
||||
#include "debug.h"
|
||||
|
||||
#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */
|
||||
#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 500 /* ms */
|
||||
|
||||
static int count;
|
||||
static struct dwc3 *dwc3_instance[DWC_CTRL_COUNT];
|
||||
|
||||
static void dwc3_check_params(struct dwc3 *dwc);
|
||||
|
||||
/**
|
||||
* dwc3_get_dr_mode - Validates and sets dr_mode
|
||||
* @dwc: pointer to our context structure
|
||||
|
|
@ -114,6 +117,7 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
|
|||
|
||||
dwc->current_dr_role = mode;
|
||||
}
|
||||
EXPORT_SYMBOL(dwc3_set_prtcap);
|
||||
|
||||
static void __dwc3_set_mode(struct work_struct *work)
|
||||
{
|
||||
|
|
@ -230,8 +234,28 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
|
|||
int retries = 1000;
|
||||
int ret;
|
||||
|
||||
usb_phy_init(dwc->usb2_phy);
|
||||
usb_phy_init(dwc->usb3_phy);
|
||||
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;
|
||||
}
|
||||
|
||||
if (dwc->maximum_speed >= USB_SPEED_SUPER) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
ret = phy_init(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
|
@ -376,6 +400,9 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc)
|
|||
evt = dwc->ev_buf;
|
||||
if (evt)
|
||||
dwc3_free_one_event_buffer(dwc, evt);
|
||||
|
||||
/* free GSI related event buffers */
|
||||
dwc3_notify_event(dwc, DWC3_GSI_EVT_BUF_FREE, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -397,6 +424,8 @@ static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length)
|
|||
}
|
||||
dwc->ev_buf = evt;
|
||||
|
||||
/* alloc GSI related event buffers */
|
||||
dwc3_notify_event(dwc, DWC3_GSI_EVT_BUF_ALLOC, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -420,6 +449,8 @@ int dwc3_event_buffers_setup(struct dwc3 *dwc)
|
|||
DWC3_GEVNTSIZ_SIZE(evt->length));
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0);
|
||||
|
||||
/* setup GSI related event buffers */
|
||||
dwc3_notify_event(dwc, DWC3_GSI_EVT_BUF_SETUP, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -436,6 +467,9 @@ void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
|
|||
dwc3_writel(dwc->regs, DWC3_GEVNTSIZ(0), DWC3_GEVNTSIZ_INTMASK
|
||||
| DWC3_GEVNTSIZ_SIZE(0));
|
||||
dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), 0);
|
||||
|
||||
/* cleanup GSI related event buffers */
|
||||
dwc3_notify_event(dwc, DWC3_GSI_EVT_BUF_CLEANUP, 0);
|
||||
}
|
||||
|
||||
static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc)
|
||||
|
|
@ -904,11 +938,24 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
|
|||
*
|
||||
* Returns 0 on success otherwise negative errno.
|
||||
*/
|
||||
static int dwc3_core_init(struct dwc3 *dwc)
|
||||
int dwc3_core_init(struct dwc3 *dwc)
|
||||
{
|
||||
u32 reg;
|
||||
int ret;
|
||||
|
||||
if (!dwc3_core_is_valid(dwc)) {
|
||||
dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
|
||||
ret = -ENODEV;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
dwc3_cache_hwparams(dwc);
|
||||
ret = dwc3_get_dr_mode(dwc);
|
||||
if (ret) {
|
||||
ret = -EINVAL;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write Linux Version Code to our GUID register so it's easy to figure
|
||||
* out which kernel version a bug was found.
|
||||
|
|
@ -957,7 +1004,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
dwc3_set_incr_burst_type(dwc);
|
||||
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 0);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 0);
|
||||
if (dwc->maximum_speed >= USB_SPEED_SUPER)
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 0);
|
||||
|
||||
ret = phy_power_on(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
goto err2;
|
||||
|
|
@ -966,12 +1015,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* ENDXFER polling is available on version 3.10a and later of
|
||||
* the DWC_usb3 controller. It is NOT available in the
|
||||
|
|
@ -1059,10 +1102,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
dwc3_check_params(dwc);
|
||||
|
||||
err4:
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
|
|
@ -1070,6 +1112,7 @@ err3:
|
|||
err2:
|
||||
usb_phy_set_suspend(dwc->usb2_phy, 1);
|
||||
usb_phy_set_suspend(dwc->usb3_phy, 1);
|
||||
dwc3_free_scratch_buffers(dwc);
|
||||
|
||||
err1:
|
||||
usb_phy_shutdown(dwc->usb2_phy);
|
||||
|
|
@ -1083,6 +1126,7 @@ err0a:
|
|||
err0:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(dwc3_core_init);
|
||||
|
||||
static int dwc3_core_get_phy(struct dwc3 *dwc)
|
||||
{
|
||||
|
|
@ -1151,7 +1195,7 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_core_init_mode(struct dwc3 *dwc)
|
||||
static int __maybe_unused dwc3_core_init_mode(struct dwc3 *dwc)
|
||||
{
|
||||
struct device *dev = dwc->dev;
|
||||
int ret;
|
||||
|
|
@ -1204,7 +1248,7 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_core_exit_mode(struct dwc3 *dwc)
|
||||
static void __maybe_unused dwc3_core_exit_mode(struct dwc3 *dwc)
|
||||
{
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
|
|
@ -1222,6 +1266,29 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
|
|||
}
|
||||
}
|
||||
|
||||
static void (*notify_event)(struct dwc3 *,
|
||||
enum dwc3_notify_event, unsigned int);
|
||||
void dwc3_set_notifier(void (*notify)(struct dwc3 *, enum dwc3_notify_event,
|
||||
unsigned int))
|
||||
{
|
||||
notify_event = notify;
|
||||
}
|
||||
EXPORT_SYMBOL(dwc3_set_notifier);
|
||||
|
||||
int dwc3_notify_event(struct dwc3 *dwc, enum dwc3_notify_event event,
|
||||
unsigned int value)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (notify_event)
|
||||
notify_event(dwc, event, value);
|
||||
else
|
||||
ret = -ENODEV;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(dwc3_notify_event);
|
||||
|
||||
static void dwc3_get_properties(struct dwc3 *dwc)
|
||||
{
|
||||
struct device *dev = dwc->dev;
|
||||
|
|
@ -1246,6 +1313,7 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
|||
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->hsphy_mode = of_usb_get_phy_mode(dev->of_node);
|
||||
|
||||
|
|
@ -1322,6 +1390,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
|
|||
&dwc->hsphy_interface);
|
||||
device_property_read_u32(dev, "snps,quirk-frame-length-adjustment",
|
||||
&dwc->fladj);
|
||||
dwc->enable_bus_suspend = device_property_read_bool(dev,
|
||||
"snps,bus-suspend-enable");
|
||||
|
||||
dwc->dis_metastability_quirk = device_property_read_bool(dev,
|
||||
"snps,dis_metastability_quirk");
|
||||
|
|
@ -1410,6 +1480,14 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
int ret;
|
||||
|
||||
void __iomem *regs;
|
||||
int irq;
|
||||
|
||||
if (count >= DWC_CTRL_COUNT) {
|
||||
dev_err(dev, "Err dwc instance %d >= %d available\n",
|
||||
count, DWC_CTRL_COUNT);
|
||||
ret = -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
dwc = devm_kzalloc(dev, sizeof(*dwc), GFP_KERNEL);
|
||||
if (!dwc)
|
||||
|
|
@ -1421,19 +1499,34 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
return -ENOMEM;
|
||||
|
||||
dwc->dev = dev;
|
||||
|
||||
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;
|
||||
dwc->xhci_resources[0].flags = res->flags;
|
||||
dwc->xhci_resources[0].name = res->name;
|
||||
|
||||
irq = platform_get_irq(to_platform_device(dwc->dev), 0);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (notify_event)
|
||||
/* will be enabled in dwc3_msm_resume() */
|
||||
disable_irq(irq);
|
||||
|
||||
dwc->irq = irq;
|
||||
/*
|
||||
* Request memory region but exclude xHCI regs,
|
||||
* since it will be requested by the xhci-plat driver.
|
||||
|
|
@ -1445,6 +1538,14 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
|
||||
dwc->dwc_wq = alloc_ordered_workqueue("dwc_wq", WQ_HIGHPRI);
|
||||
if (!dwc->dwc_wq) {
|
||||
dev_err(dev,
|
||||
"%s: Unable to create workqueue dwc_wq\n", __func__);
|
||||
goto err0;
|
||||
}
|
||||
|
||||
INIT_WORK(&dwc->bh_work, dwc3_bh_work);
|
||||
dwc->regs = regs;
|
||||
dwc->regs_size = resource_size(&dwc_res);
|
||||
|
||||
|
|
@ -1459,7 +1560,7 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
|
||||
ret = devm_clk_bulk_get(dev, dwc->num_clks, dwc->clks);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
goto err0;
|
||||
/*
|
||||
* Clocks are optional, but new DT platforms should support all
|
||||
* clocks as required by the DT-binding.
|
||||
|
|
@ -1470,61 +1571,59 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
|
||||
ret = reset_control_deassert(dwc->reset);
|
||||
if (ret)
|
||||
return ret;
|
||||
goto err0;
|
||||
|
||||
ret = clk_bulk_prepare_enable(dwc->num_clks, dwc->clks);
|
||||
if (ret)
|
||||
goto assert_reset;
|
||||
|
||||
if (!dwc3_core_is_valid(dwc)) {
|
||||
dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n");
|
||||
ret = -ENODEV;
|
||||
goto disable_clks;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, dwc);
|
||||
dwc3_cache_hwparams(dwc);
|
||||
|
||||
spin_lock_init(&dwc->lock);
|
||||
|
||||
pm_runtime_no_callbacks(dev);
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
pm_runtime_set_autosuspend_delay(dev, DWC3_DEFAULT_AUTOSUSPEND_DELAY);
|
||||
if (dwc->enable_bus_suspend) {
|
||||
pm_runtime_set_autosuspend_delay(dev,
|
||||
DWC3_DEFAULT_AUTOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(dev);
|
||||
}
|
||||
pm_runtime_enable(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 err2;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
ret = dwc3_get_dr_mode(dwc);
|
||||
if (ret)
|
||||
goto err3;
|
||||
|
||||
ret = dwc3_alloc_scratch_buffers(dwc);
|
||||
if (ret)
|
||||
goto err3;
|
||||
goto err2;
|
||||
|
||||
ret = dwc3_core_init(dwc);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to initialize core: %d\n", ret);
|
||||
goto err4;
|
||||
if (!notify_event) {
|
||||
ret = dwc3_core_init(dwc);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to initialize core: %d\n",
|
||||
ret);
|
||||
goto err3;
|
||||
}
|
||||
|
||||
ret = dwc3_event_buffers_setup(dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to setup event buffers\n");
|
||||
goto err3;
|
||||
}
|
||||
|
||||
ret = dwc3_core_init_mode(dwc);
|
||||
if (ret) {
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
goto err3;
|
||||
}
|
||||
}
|
||||
|
||||
dwc3_check_params(dwc);
|
||||
|
||||
ret = dwc3_core_init_mode(dwc);
|
||||
if (ret)
|
||||
goto err5;
|
||||
|
||||
dwc3_debugfs_init(dwc);
|
||||
|
||||
dwc->dwc_ipc_log_ctxt = ipc_log_context_create(NUM_LOG_PAGES,
|
||||
|
|
@ -1536,32 +1635,22 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
dwc->index = count;
|
||||
count++;
|
||||
|
||||
pm_runtime_put(dev);
|
||||
|
||||
pm_runtime_allow(dev);
|
||||
return 0;
|
||||
|
||||
err5:
|
||||
dwc3_event_buffers_cleanup(dwc);
|
||||
dwc3_ulpi_exit(dwc);
|
||||
|
||||
err4:
|
||||
dwc3_free_scratch_buffers(dwc);
|
||||
|
||||
err3:
|
||||
dwc3_free_event_buffers(dwc);
|
||||
|
||||
dwc3_free_scratch_buffers(dwc);
|
||||
err2:
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
|
||||
dwc3_free_event_buffers(dwc);
|
||||
err1:
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
disable_clks:
|
||||
clk_bulk_disable_unprepare(dwc->num_clks, dwc->clks);
|
||||
assert_reset:
|
||||
reset_control_assert(dwc->reset);
|
||||
|
||||
destroy_workqueue(dwc->dwc_wq);
|
||||
err0:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1569,15 +1658,8 @@ static int dwc3_remove(struct platform_device *pdev)
|
|||
{
|
||||
struct dwc3 *dwc = platform_get_drvdata(pdev);
|
||||
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
dwc3_debugfs_exit(dwc);
|
||||
dwc3_core_exit_mode(dwc);
|
||||
|
||||
dwc3_core_exit(dwc);
|
||||
dwc3_ulpi_exit(dwc);
|
||||
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
|
|
@ -1764,6 +1846,10 @@ static int dwc3_runtime_suspend(struct device *dev)
|
|||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
/* 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;
|
||||
|
||||
if (dwc3_runtime_checks(dwc))
|
||||
return -EBUSY;
|
||||
|
||||
|
|
@ -1781,6 +1867,10 @@ static int dwc3_runtime_resume(struct device *dev)
|
|||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
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;
|
||||
|
||||
device_init_wakeup(dev, false);
|
||||
|
||||
ret = dwc3_resume_common(dwc, PMSG_AUTO_RESUME);
|
||||
|
|
@ -1830,6 +1920,10 @@ static int dwc3_suspend(struct device *dev)
|
|||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
/* 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;
|
||||
|
||||
ret = dwc3_suspend_common(dwc, PMSG_SUSPEND);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -1844,6 +1938,10 @@ static int dwc3_resume(struct device *dev)
|
|||
struct dwc3 *dwc = dev_get_drvdata(dev);
|
||||
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);
|
||||
|
||||
ret = dwc3_resume_common(dwc, PMSG_RESUME);
|
||||
|
|
|
|||
|
|
@ -162,8 +162,20 @@
|
|||
#define DWC3_OEVTEN 0xcc0C
|
||||
#define DWC3_OSTS 0xcc10
|
||||
|
||||
/* DWC 3.1 Link Registers */
|
||||
#define DWC31_LINK_LU3LFPSRXTIM(n) (0xd010 + ((n) * 0x80))
|
||||
#define GEN2_U3_EXIT_RSP_RX_CLK(n) ((n) << 16)
|
||||
#define GEN2_U3_EXIT_RSP_RX_CLK_MASK GEN2_U3_EXIT_RSP_RX_CLK(0xff)
|
||||
#define GEN1_U3_EXIT_RSP_RX_CLK(n) (n)
|
||||
#define GEN1_U3_EXIT_RSP_RX_CLK_MASK GEN1_U3_EXIT_RSP_RX_CLK(0xff)
|
||||
#define DWC31_LINK_GDBGLTSSM 0xd050
|
||||
|
||||
/* Bit fields */
|
||||
|
||||
/* Global SoC Bus Configuration Register 1 */
|
||||
#define DWC3_GSBUSCFG1_PIPETRANSLIMIT_MASK (0x0f << 8)
|
||||
#define DWC3_GSBUSCFG1_PIPETRANSLIMIT(n) ((n) << 8)
|
||||
|
||||
/* Global SoC Bus Configuration INCRx Register 0 */
|
||||
#define DWC3_GSBUSCFG0_INCR256BRSTENA (1 << 7) /* INCR256 burst */
|
||||
#define DWC3_GSBUSCFG0_INCR128BRSTENA (1 << 6) /* INCR128 burst */
|
||||
|
|
@ -265,6 +277,9 @@
|
|||
#define DWC3_GSTS_CURMOD_DEVICE 0
|
||||
#define DWC3_GSTS_CURMOD_HOST 1
|
||||
|
||||
/* Global Debug LTSSM Register */
|
||||
#define DWC3_GDBGLTSSM_LINKSTATE_MASK (0xF << 22)
|
||||
|
||||
/* Global USB2 PHY Configuration Register */
|
||||
#define DWC3_GUSB2PHYCFG_PHYSOFTRST BIT(31)
|
||||
#define DWC3_GUSB2PHYCFG_U2_FREECLK_EXISTS BIT(30)
|
||||
|
|
@ -294,6 +309,7 @@
|
|||
#define DWC3_GUSB3PIPECTL_DISRXDETINP3 BIT(28)
|
||||
#define DWC3_GUSB3PIPECTL_UX_EXIT_PX BIT(27)
|
||||
#define DWC3_GUSB3PIPECTL_REQP1P2P3 BIT(24)
|
||||
#define DWC3_GUSB3PIPECTL_DISRXDETU3 BIT(22)
|
||||
#define DWC3_GUSB3PIPECTL_DEP1P2P3(n) ((n) << 19)
|
||||
#define DWC3_GUSB3PIPECTL_DEP1P2P3_MASK DWC3_GUSB3PIPECTL_DEP1P2P3(7)
|
||||
#define DWC3_GUSB3PIPECTL_DEP1P2P3_EN DWC3_GUSB3PIPECTL_DEP1P2P3(1)
|
||||
|
|
@ -303,6 +319,7 @@
|
|||
#define DWC3_GUSB3PIPECTL_RX_DETOPOLL BIT(8)
|
||||
#define DWC3_GUSB3PIPECTL_TX_DEEPH_MASK DWC3_GUSB3PIPECTL_TX_DEEPH(3)
|
||||
#define DWC3_GUSB3PIPECTL_TX_DEEPH(n) ((n) << 1)
|
||||
#define DWC3_GUSB3PIPECTL_DELAYP1TRANS BIT(18)
|
||||
|
||||
/* Global TX Fifo Size Register */
|
||||
#define DWC31_GTXFIFOSIZ_TXFRAMNUM BIT(15) /* DWC_usb31 only */
|
||||
|
|
@ -483,6 +500,7 @@
|
|||
#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO 0x04
|
||||
#define DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI 0x05
|
||||
|
||||
#define DWC3_DGCMD_XMIT_DEV 0x07
|
||||
#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09
|
||||
#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a
|
||||
#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c
|
||||
|
|
@ -683,10 +701,13 @@ struct dwc3_ep_events {
|
|||
* @pending_list: list of pending requests for this endpoint
|
||||
* @started_list: list of started requests on this endpoint
|
||||
* @regs: pointer to first endpoint register
|
||||
* @trb_dma_pool: dma pool used to get aligned trb memory pool
|
||||
* @trb_pool: array of transaction buffers
|
||||
* @trb_pool_dma: dma address of @trb_pool
|
||||
* @num_trbs: num of trbs in the trb dma pool
|
||||
* @trb_enqueue: enqueue 'pointer' into TRB array
|
||||
* @trb_dequeue: dequeue 'pointer' into TRB array
|
||||
* @desc: usb_endpoint_descriptor pointer
|
||||
* @dwc: pointer to DWC controller
|
||||
* @saved_state: ep state saved during hibernation
|
||||
* @flags: endpoint flags (wedged, stalled, ...)
|
||||
|
|
@ -714,8 +735,10 @@ struct dwc3_ep {
|
|||
|
||||
void __iomem *regs;
|
||||
|
||||
struct dma_pool *trb_dma_pool;
|
||||
struct dwc3_trb *trb_pool;
|
||||
dma_addr_t trb_pool_dma;
|
||||
u32 num_trbs;
|
||||
struct dwc3 *dwc;
|
||||
|
||||
u32 saved_state;
|
||||
|
|
@ -751,6 +774,7 @@ struct dwc3_ep {
|
|||
|
||||
unsigned direction:1;
|
||||
unsigned stream_capable:1;
|
||||
unsigned gsi:1;
|
||||
|
||||
/* For isochronous START TRANSFER workaround only */
|
||||
u8 combo_num;
|
||||
|
|
@ -759,6 +783,7 @@ struct dwc3_ep {
|
|||
struct dwc3_ep_events dbg_ep_events;
|
||||
struct dwc3_ep_events dbg_ep_events_diff;
|
||||
ktime_t dbg_ep_events_kt;
|
||||
int fifo_depth;
|
||||
};
|
||||
|
||||
enum dwc3_phy {
|
||||
|
|
@ -946,7 +971,7 @@ struct dwc3_scratchpad_array {
|
|||
__le64 dma_adr[DWC3_MAX_HIBER_SCRATCHBUFS];
|
||||
};
|
||||
|
||||
#define MAX_INTR_STATS 10
|
||||
#define MAX_INTR_STATS 10
|
||||
|
||||
/**
|
||||
* struct dwc3 - representation of our controller
|
||||
|
|
@ -980,9 +1005,11 @@ struct dwc3_scratchpad_array {
|
|||
* @current_otg_role: current role of operation while using the OTG block
|
||||
* @desired_otg_role: desired role of operation while using the OTG block
|
||||
* @otg_restart_host: flag that OTG controller needs to restart host
|
||||
* @reg_phys: physical base address of dwc3 core register address space
|
||||
* @nr_scratch: number of scratch buffers
|
||||
* @u1u2: only used on revisions <1.83a for workaround
|
||||
* @maximum_speed: maximum speed requested (mainly for testing purposes)
|
||||
* @maximum_speed: maximum speed to operate as requested by sw
|
||||
* @max_hw_supp_speed: maximum speed supported by hw design
|
||||
* @revision: revision register contents
|
||||
* @version_type: VERSIONTYPE register contents, a sub release of a revision
|
||||
* @dr_mode: requested mode of operation
|
||||
|
|
@ -1071,6 +1098,14 @@ struct dwc3_scratchpad_array {
|
|||
* 2 - No de-emphasis
|
||||
* 3 - Reserved
|
||||
* @dis_metastability_quirk: set to disable metastability quirk.
|
||||
* @err_evt_seen: previous event in queue was erratic error
|
||||
* @in_lpm: indicates if controller is in low power mode (no clocks)
|
||||
* @irq: irq number
|
||||
* @irq_cnt: total irq count
|
||||
* @bh_completion_time: time taken for IRQ bottom-half completion
|
||||
* @bh_handled_evt_cnt: no. of events handled per IRQ bottom-half
|
||||
* @irq_dbg_index: index for capturing IRQ stats
|
||||
* @vbus_draw: current to be drawn from USB
|
||||
* @imod_interval: set the interrupt moderation interval in 250ns
|
||||
* increments or 0 to disable.
|
||||
* @xhci_imod_value: imod value to use with xhci
|
||||
|
|
@ -1126,6 +1161,7 @@ struct dwc3 {
|
|||
|
||||
void __iomem *regs;
|
||||
size_t regs_size;
|
||||
phys_addr_t reg_phys;
|
||||
|
||||
enum usb_dr_mode dr_mode;
|
||||
u32 current_dr_role;
|
||||
|
|
@ -1143,6 +1179,7 @@ struct dwc3 {
|
|||
u32 nr_scratch;
|
||||
u32 u1u2;
|
||||
u32 maximum_speed;
|
||||
u32 max_hw_supp_speed;
|
||||
|
||||
/*
|
||||
* All 3.1 IP version constants are greater than the 3.0 IP
|
||||
|
|
@ -1265,24 +1302,42 @@ struct dwc3 {
|
|||
|
||||
unsigned tx_de_emphasis_quirk:1;
|
||||
unsigned tx_de_emphasis:2;
|
||||
unsigned err_evt_seen:1;
|
||||
unsigned enable_bus_suspend:1;
|
||||
|
||||
atomic_t in_lpm;
|
||||
bool b_suspend;
|
||||
unsigned int vbus_draw;
|
||||
|
||||
unsigned dis_metastability_quirk:1;
|
||||
|
||||
u16 imod_interval;
|
||||
u32 xhci_imod_value;
|
||||
|
||||
struct workqueue_struct *dwc_wq;
|
||||
struct work_struct bh_work;
|
||||
|
||||
unsigned long ep_cmd_timeout_cnt;
|
||||
|
||||
unsigned int index;
|
||||
void *dwc_ipc_log_ctxt;
|
||||
struct dwc3_gadget_events dbg_gadget_events;
|
||||
|
||||
/* IRQ timing statistics */
|
||||
int irq;
|
||||
unsigned long irq_cnt;
|
||||
unsigned int bh_completion_time[MAX_INTR_STATS];
|
||||
unsigned int bh_handled_evt_cnt[MAX_INTR_STATS];
|
||||
ktime_t irq_start_time[MAX_INTR_STATS];
|
||||
ktime_t t_pwr_evt_irq;
|
||||
unsigned int irq_completion_time[MAX_INTR_STATS];
|
||||
unsigned int irq_event_count[MAX_INTR_STATS];
|
||||
unsigned int irq_dbg_index;
|
||||
|
||||
/* Indicate if the gadget was powered by the otg driver */
|
||||
unsigned int vbus_active:1;
|
||||
/* Indicate if software connect was issued by the usb_gadget_driver */
|
||||
unsigned int softconnect:1;
|
||||
};
|
||||
|
||||
#define INCRX_BURST_MODE 0
|
||||
|
|
@ -1483,11 +1538,16 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
|
|||
int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
|
||||
struct dwc3_gadget_ep_cmd_params *params);
|
||||
int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param);
|
||||
void dwc3_gadget_disable_irq(struct dwc3 *dwc);
|
||||
int dwc3_core_init(struct dwc3 *dwc);
|
||||
int dwc3_event_buffers_setup(struct dwc3 *dwc);
|
||||
#else
|
||||
static inline int dwc3_gadget_init(struct dwc3 *dwc)
|
||||
{ return 0; }
|
||||
static inline void dwc3_gadget_exit(struct dwc3 *dwc)
|
||||
{ }
|
||||
static inline void dwc3_gadget_restart(struct dwc3 *dwc)
|
||||
{ }
|
||||
static inline int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
|
||||
{ return 0; }
|
||||
static inline int dwc3_gadget_get_link_state(struct dwc3 *dwc)
|
||||
|
|
@ -1502,6 +1562,12 @@ static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
|
|||
static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
|
||||
int cmd, u32 param)
|
||||
{ return 0; }
|
||||
static inline void dwc3_gadget_disable_irq(struct dwc3 *dwc)
|
||||
{ }
|
||||
static int dwc3_core_init(struct dwc3 *dwc)
|
||||
{ return 0; }
|
||||
static int dwc3_event_buffers_setup(struct dwc3 *dwc)
|
||||
{ return 0; }
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
|
||||
|
|
@ -1557,4 +1623,29 @@ static inline void dwc3_ulpi_exit(struct dwc3 *dwc)
|
|||
{ }
|
||||
#endif
|
||||
|
||||
enum dwc3_notify_event {
|
||||
DWC3_CONTROLLER_ERROR_EVENT,
|
||||
DWC3_CONTROLLER_RESET_EVENT,
|
||||
DWC3_CONTROLLER_POST_RESET_EVENT,
|
||||
DWC3_CORE_PM_SUSPEND_EVENT,
|
||||
DWC3_CORE_PM_RESUME_EVENT,
|
||||
DWC3_CONTROLLER_CONNDONE_EVENT,
|
||||
DWC3_CONTROLLER_NOTIFY_OTG_EVENT,
|
||||
DWC3_CONTROLLER_SET_CURRENT_DRAW_EVENT,
|
||||
DWC3_CONTROLLER_NOTIFY_DISABLE_UPDXFER,
|
||||
|
||||
/* USB GSI event buffer related notification */
|
||||
DWC3_GSI_EVT_BUF_ALLOC,
|
||||
DWC3_GSI_EVT_BUF_SETUP,
|
||||
DWC3_GSI_EVT_BUF_CLEANUP,
|
||||
DWC3_GSI_EVT_BUF_CLEAR,
|
||||
DWC3_GSI_EVT_BUF_FREE,
|
||||
DWC3_CONTROLLER_NOTIFY_CLEAR_DB,
|
||||
};
|
||||
|
||||
extern void dwc3_set_notifier(void (*notify)(struct dwc3 *dwc3,
|
||||
enum dwc3_notify_event event,
|
||||
unsigned int value));
|
||||
extern int dwc3_notify_event(struct dwc3 *dwc3, enum dwc3_notify_event event,
|
||||
unsigned int value);
|
||||
#endif /* __DRIVERS_USB_DWC3_CORE_H */
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ void dwc3_dbg_print(struct dwc3 *dwc, u8 ep_num, const char *name,
|
|||
ipc_log_string(dwc->dwc_ipc_log_ctxt, "%02X %-25.25s %4i ?\t%s",
|
||||
ep_num, name, status, extra);
|
||||
}
|
||||
EXPORT_SYMBOL(dwc3_dbg_print);
|
||||
|
||||
/**
|
||||
* dwc3_dbg_done: prints a DONE event
|
||||
|
|
|
|||
|
|
@ -331,6 +331,11 @@ static int dwc3_lsp_show(struct seq_file *s, void *unused)
|
|||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
if (atomic_read(&dwc->in_lpm)) {
|
||||
seq_puts(s, "USB device is powered off\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GSTS);
|
||||
current_mode = DWC3_GSTS_CURMOD(reg);
|
||||
|
|
@ -394,6 +399,11 @@ static int dwc3_mode_show(struct seq_file *s, void *unused)
|
|||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
if (atomic_read(&dwc->in_lpm)) {
|
||||
seq_puts(s, "USB device is powered off\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
|
@ -428,6 +438,11 @@ static ssize_t dwc3_mode_write(struct file *file,
|
|||
u32 mode = 0;
|
||||
char buf[32] = {};
|
||||
|
||||
if (atomic_read(&dwc->in_lpm)) {
|
||||
dev_err(dwc->dev, "USB device is powered off\n");
|
||||
return count;
|
||||
}
|
||||
|
||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
||||
|
|
@ -459,6 +474,11 @@ static int dwc3_testmode_show(struct seq_file *s, void *unused)
|
|||
unsigned long flags;
|
||||
u32 reg;
|
||||
|
||||
if (atomic_read(&dwc->in_lpm)) {
|
||||
seq_puts(s, "USB device is powered off\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg &= DWC3_DCTL_TSTCTRL_MASK;
|
||||
|
|
@ -505,6 +525,11 @@ static ssize_t dwc3_testmode_write(struct file *file,
|
|||
u32 testmode = 0;
|
||||
char buf[32] = {};
|
||||
|
||||
if (atomic_read(&dwc->in_lpm)) {
|
||||
seq_puts(s, "USB device is powered off\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
||||
|
|
@ -544,6 +569,11 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
|
|||
u32 reg;
|
||||
u8 speed;
|
||||
|
||||
if (atomic_read(&dwc->in_lpm)) {
|
||||
seq_puts(s, "USB device is powered off\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GSTS);
|
||||
if (DWC3_GSTS_CURMOD(reg) != DWC3_GSTS_CURMOD_DEVICE) {
|
||||
|
|
@ -580,6 +610,11 @@ static ssize_t dwc3_link_state_write(struct file *file,
|
|||
u32 reg;
|
||||
u8 speed;
|
||||
|
||||
if (atomic_read(&dwc->in_lpm)) {
|
||||
seq_puts(s, "USB device is powered off\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
|
||||
return -EFAULT;
|
||||
|
||||
|
|
@ -640,6 +675,11 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused)
|
|||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
if (atomic_read(&dwc->in_lpm)) {
|
||||
seq_puts(s, "USB device is powered off\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_TXFIFO);
|
||||
|
||||
|
|
@ -659,6 +699,11 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused)
|
|||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
if (atomic_read(&dwc->in_lpm)) {
|
||||
seq_puts(s, "USB device is powered off\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_RXFIFO);
|
||||
|
||||
|
|
@ -693,6 +738,11 @@ static int dwc3_rx_request_queue_show(struct seq_file *s, void *unused)
|
|||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
if (atomic_read(&dwc->in_lpm)) {
|
||||
seq_puts(s, "USB device is powered off\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_RXREQQ);
|
||||
seq_printf(s, "%u\n", val);
|
||||
|
|
@ -708,6 +758,11 @@ static int dwc3_rx_info_queue_show(struct seq_file *s, void *unused)
|
|||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
if (atomic_read(&dwc->in_lpm)) {
|
||||
seq_puts(s, "USB device is powered off\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_RXINFOQ);
|
||||
seq_printf(s, "%u\n", val);
|
||||
|
|
@ -723,6 +778,11 @@ static int dwc3_descriptor_fetch_queue_show(struct seq_file *s, void *unused)
|
|||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
if (atomic_read(&dwc->in_lpm)) {
|
||||
seq_puts(s, "USB device is powered off\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_DESCFETCHQ);
|
||||
seq_printf(s, "%u\n", val);
|
||||
|
|
@ -738,6 +798,11 @@ static int dwc3_event_queue_show(struct seq_file *s, void *unused)
|
|||
unsigned long flags;
|
||||
u32 val;
|
||||
|
||||
if (atomic_read(&dwc->in_lpm)) {
|
||||
seq_puts(s, "USB device is powered off\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
val = dwc3_core_fifo_space(dep, DWC3_EVENTQ);
|
||||
seq_printf(s, "%u\n", val);
|
||||
|
|
@ -1052,6 +1117,11 @@ static int dwc3_gadget_int_events_show(struct seq_file *s, void *unused)
|
|||
seq_printf(s, "%d\t", dwc->bh_completion_time[i]);
|
||||
seq_putc(s, '\n');
|
||||
|
||||
seq_printf(s, "t_pwr evt irq : %lld\n",
|
||||
ktime_to_us(dwc->t_pwr_evt_irq));
|
||||
|
||||
seq_printf(s, "ep_cmd_timeout_cnt : %lu\n", dwc->ep_cmd_timeout_cnt);
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -248,6 +248,7 @@ int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
|
|||
struct dwc3_ep *dep = to_dwc3_ep(ep);
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
||||
dbg_event(dep->number, "EP0STAL", value);
|
||||
dwc3_ep0_stall_and_restart(dwc);
|
||||
|
||||
return 0;
|
||||
|
|
@ -278,7 +279,8 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
|
|||
dwc3_ep0_prepare_one_trb(dep, dwc->ep0_trb_addr, 8,
|
||||
DWC3_TRBCTL_CONTROL_SETUP, false);
|
||||
ret = dwc3_ep0_start_trans(dep);
|
||||
WARN_ON(ret < 0);
|
||||
if (WARN_ON(ret < 0))
|
||||
dbg_event(dwc->eps[0]->number, "EOUTSTART", ret);
|
||||
}
|
||||
|
||||
static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)
|
||||
|
|
@ -691,7 +693,8 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req)
|
|||
/* now that we have the time, issue DGCMD Set Sel */
|
||||
ret = dwc3_send_gadget_generic_command(dwc,
|
||||
DWC3_DGCMD_SET_PERIODIC_PAR, param);
|
||||
WARN_ON(ret < 0);
|
||||
if (WARN_ON(ret < 0))
|
||||
dbg_event(dep->number, "ESET_SELCMPL", ret);
|
||||
}
|
||||
|
||||
static int dwc3_ep0_set_sel(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
|
||||
|
|
@ -803,6 +806,7 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
|
|||
dwc->ep0_next_event = DWC3_EP0_NRDY_DATA;
|
||||
}
|
||||
|
||||
dbg_setup(0x00, ctrl);
|
||||
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
|
||||
ret = dwc3_ep0_std_request(dwc, ctrl);
|
||||
else
|
||||
|
|
@ -812,8 +816,16 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
|
|||
dwc->delayed_status = true;
|
||||
|
||||
out:
|
||||
if (ret < 0)
|
||||
/*
|
||||
* Don't try to halt ep0 if ret is -ESHUTDOWN.
|
||||
* ret as -ESHUTDOWN suggests that setup packet related response
|
||||
* is available but queueing of ep0 is failed. Possibly ep0 is
|
||||
* already disabled.
|
||||
*/
|
||||
if (ret < 0 && ret != -ESHUTDOWN) {
|
||||
dbg_event(0x0, "ERRSTAL", ret);
|
||||
dwc3_ep0_stall_and_restart(dwc);
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
||||
|
|
@ -842,10 +854,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
|||
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
|
||||
if (status == DWC3_TRBSTS_SETUP_PENDING) {
|
||||
dwc->setup_packet_pending = true;
|
||||
if (r)
|
||||
dwc3_gadget_giveback(ep0, r, -ECONNRESET);
|
||||
|
||||
return;
|
||||
dbg_event(0x0, "SETUPPEND", status);
|
||||
}
|
||||
|
||||
ur = &r->request;
|
||||
|
|
@ -868,10 +877,13 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
|
|||
dwc->ep0_bounced = false;
|
||||
}
|
||||
|
||||
if ((epnum & 1) && ur->actual < ur->length)
|
||||
if ((epnum & 1) && ur->actual < ur->length) {
|
||||
/* for some reason we did not get everything out */
|
||||
dbg_event(epnum, "INDATSTAL", 0);
|
||||
dwc3_ep0_stall_and_restart(dwc);
|
||||
else
|
||||
} else {
|
||||
dwc3_gadget_giveback(ep0, r, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_ep0_complete_status(struct dwc3 *dwc,
|
||||
|
|
@ -900,6 +912,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
|
|||
if (ret < 0) {
|
||||
dev_err(dwc->dev, "invalid test #%d\n",
|
||||
dwc->test_mode_nr);
|
||||
dbg_event(0x00, "INVALTEST", ret);
|
||||
dwc3_ep0_stall_and_restart(dwc);
|
||||
return;
|
||||
}
|
||||
|
|
@ -909,6 +922,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
|
|||
if (status == DWC3_TRBSTS_SETUP_PENDING)
|
||||
dwc->setup_packet_pending = true;
|
||||
|
||||
dbg_print(dep->number, "DONE", status, "STATUS");
|
||||
dwc->ep0state = EP0_SETUP_PHASE;
|
||||
dwc3_ep0_out_start(dwc);
|
||||
}
|
||||
|
|
@ -1015,6 +1029,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
|
|||
}
|
||||
|
||||
WARN_ON(ret < 0);
|
||||
dbg_queue(dep->number, &req->request, ret);
|
||||
}
|
||||
|
||||
static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
|
||||
|
|
@ -1031,7 +1046,11 @@ static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
|
|||
|
||||
static void __dwc3_ep0_do_control_status(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
{
|
||||
WARN_ON(dwc3_ep0_start_control_status(dep));
|
||||
int ret;
|
||||
|
||||
ret = dwc3_ep0_start_control_status(dep);
|
||||
if (WARN_ON_ONCE(ret))
|
||||
dbg_event(dep->number, "ECTRLSTATUS", ret);
|
||||
}
|
||||
|
||||
static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
|
||||
|
|
@ -1056,7 +1075,11 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
|
|||
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
|
||||
WARN_ON_ONCE(ret);
|
||||
if (ret) {
|
||||
dev_dbg(dwc->dev, "%s: send ep cmd ENDTRANSFER failed",
|
||||
dep->name);
|
||||
dbg_event(dep->number, "EENDXFER", ret);
|
||||
}
|
||||
dep->resource_index = 0;
|
||||
}
|
||||
|
||||
|
|
@ -1087,6 +1110,7 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
|
|||
|
||||
dev_err(dwc->dev, "unexpected direction for Data Phase\n");
|
||||
dwc3_ep0_end_control_data(dwc, dep);
|
||||
dbg_event(epnum, "WRONGDR", 0);
|
||||
dwc3_ep0_stall_and_restart(dwc);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1103,7 +1127,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
|
|||
if (dwc->delayed_status) {
|
||||
struct dwc3_ep *dep = dwc->eps[0];
|
||||
|
||||
WARN_ON_ONCE(event->endpoint_number != 1);
|
||||
if (event->endpoint_number != 1)
|
||||
dbg_event(epnum, "EEPNUM", event->status);
|
||||
/*
|
||||
* We should handle the delay STATUS phase here if the
|
||||
* request for handling delay STATUS has been queued
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@
|
|||
#define DWC3_ALIGN_FRAME(d, n) (((d)->frame_number + ((d)->interval * (n))) \
|
||||
& ~((d)->interval - 1))
|
||||
|
||||
static int __dwc3_gadget_start(struct dwc3 *dwc);
|
||||
static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc);
|
||||
|
||||
/**
|
||||
* dwc3_gadget_set_test_mode - enables usb2 test modes
|
||||
* @dwc: pointer to our context structure
|
||||
|
|
@ -187,9 +190,6 @@ static void dwc3_gadget_del_and_unmap_request(struct dwc3_ep *dep,
|
|||
|
||||
req->trb = NULL;
|
||||
trace_dwc3_gadget_giveback(req);
|
||||
|
||||
if (dep->number > 1)
|
||||
pm_runtime_put(dwc->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -270,7 +270,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
|
|||
{
|
||||
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
u32 timeout = 1000;
|
||||
u32 timeout = 3000;
|
||||
u32 saved_config = 0;
|
||||
u32 reg;
|
||||
|
||||
|
|
@ -379,6 +379,9 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
|
|||
|
||||
if (timeout == 0) {
|
||||
ret = -ETIMEDOUT;
|
||||
dev_err(dwc->dev, "%s command timeout for %s\n",
|
||||
dwc3_gadget_ep_cmd_string(cmd), dep->name);
|
||||
dwc->ep_cmd_timeout_cnt++;
|
||||
cmd_status = -ETIMEDOUT;
|
||||
}
|
||||
|
||||
|
|
@ -397,6 +400,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
|
|||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(dwc3_send_gadget_ep_cmd);
|
||||
|
||||
static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
|
||||
{
|
||||
|
|
@ -421,14 +425,6 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
|
|||
return dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
|
||||
}
|
||||
|
||||
static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
|
||||
struct dwc3_trb *trb)
|
||||
{
|
||||
u32 offset = (char *) trb - (char *) dep->trb_pool;
|
||||
|
||||
return dep->trb_pool_dma + offset;
|
||||
}
|
||||
|
||||
static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
|
@ -444,6 +440,7 @@ static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
|
|||
dep->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
dep->num_trbs = DWC3_TRB_NUM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -452,11 +449,27 @@ static void dwc3_free_trb_pool(struct dwc3_ep *dep)
|
|||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
||||
dma_free_coherent(dwc->sysdev, sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
|
||||
dep->trb_pool, dep->trb_pool_dma);
|
||||
/* Freeing of GSI EP TRBs are handled by GSI EP ops. */
|
||||
if (dep->gsi)
|
||||
return;
|
||||
|
||||
dep->trb_pool = NULL;
|
||||
dep->trb_pool_dma = 0;
|
||||
/*
|
||||
* Clean up ep ring to avoid getting xferInProgress due to stale trbs
|
||||
* with HWO bit set from previous composition when update transfer cmd
|
||||
* is issued.
|
||||
*/
|
||||
if (dep->number > 1 && dep->trb_pool && dep->trb_pool_dma) {
|
||||
memset(&dep->trb_pool[0], 0,
|
||||
sizeof(struct dwc3_trb) * dep->num_trbs);
|
||||
dbg_event(dep->number, "Clr_TRB", 0);
|
||||
dev_dbg(dwc->dev, "Clr_TRB ring of %s\n", dep->name);
|
||||
|
||||
dma_free_coherent(dwc->sysdev,
|
||||
sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
|
||||
dep->trb_pool, dep->trb_pool_dma);
|
||||
dep->trb_pool = NULL;
|
||||
dep->trb_pool_dma = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int dwc3_gadget_set_xfer_resource(struct dwc3_ep *dep)
|
||||
|
|
@ -618,13 +631,18 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
|
|||
|
||||
if (!(dep->flags & DWC3_EP_ENABLED)) {
|
||||
ret = dwc3_gadget_start_config(dep);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "start_config() failed for %s\n",
|
||||
dep->name);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = dwc3_gadget_set_ep_config(dep, action);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "set_ep_config() failed for %s\n", dep->name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!(dep->flags & DWC3_EP_ENABLED)) {
|
||||
struct dwc3_trb *trb_st_hw;
|
||||
|
|
@ -687,8 +705,6 @@ out:
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
|
||||
bool interrupt);
|
||||
static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3_request *req;
|
||||
|
|
@ -792,6 +808,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
|
|||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
|
||||
dbg_event(dep->number, "ENABLE", ret);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return ret;
|
||||
|
|
@ -819,6 +836,7 @@ static int dwc3_gadget_ep_disable(struct usb_ep *ep)
|
|||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
ret = __dwc3_gadget_ep_disable(dep);
|
||||
dbg_event(dep->number, "DISABLE", ret);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return ret;
|
||||
|
|
@ -1215,6 +1233,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
|
|||
{
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
struct dwc3_request *req;
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
int starting;
|
||||
int ret;
|
||||
u32 cmd;
|
||||
|
|
@ -1228,6 +1247,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
|
|||
req = next_request(&dep->started_list);
|
||||
if (!req) {
|
||||
dep->flags |= DWC3_EP_PENDING_REQUEST;
|
||||
dbg_event(dep->number, "NO REQ", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1438,8 +1458,6 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|||
dep->name, &req->request))
|
||||
return -EINVAL;
|
||||
|
||||
pm_runtime_get(dwc->dev);
|
||||
|
||||
req->request.actual = 0;
|
||||
req->request.status = -EINPROGRESS;
|
||||
|
||||
|
|
@ -1537,6 +1555,11 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
|
|||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
if (atomic_read(&dwc->in_lpm)) {
|
||||
dev_err(dwc->dev, "Unable to dequeue while in LPM\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
trace_dwc3_ep_dequeue(req);
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
|
@ -1571,6 +1594,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
|
|||
}
|
||||
|
||||
out1:
|
||||
dbg_event(dep->number, "DEQUEUE", 0);
|
||||
dwc3_gadget_giveback(dep, req, -ECONNRESET);
|
||||
|
||||
out0:
|
||||
|
|
@ -1585,13 +1609,18 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
|
|||
struct dwc3 *dwc = dep->dwc;
|
||||
int ret;
|
||||
|
||||
if (!dep->endpoint.desc) {
|
||||
dev_dbg(dwc->dev, "(%s)'s desc is NULL.\n", dep->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
|
||||
dev_err(dwc->dev, "%s is of Isochronous type\n", dep->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(¶ms, 0x00, sizeof(params));
|
||||
|
||||
dbg_event(dep->number, "HALT", value);
|
||||
if (value) {
|
||||
struct dwc3_trb *trb;
|
||||
|
||||
|
|
@ -1655,6 +1684,7 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
|
|||
int ret;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
dbg_event(dep->number, "WEDGE", 0);
|
||||
dep->flags |= DWC3_EP_WEDGE;
|
||||
|
||||
if (dep->number == 0 || dep->number == 1)
|
||||
|
|
@ -1797,14 +1827,34 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void dwc3_stop_active_transfers(struct dwc3 *dwc)
|
||||
{
|
||||
u32 epnum;
|
||||
|
||||
for (epnum = 2; epnum < DWC3_ENDPOINTS_NUM; epnum++) {
|
||||
struct dwc3_ep *dep;
|
||||
|
||||
dep = dwc->eps[epnum];
|
||||
if (!dep)
|
||||
continue;
|
||||
|
||||
if (!(dep->flags & DWC3_EP_ENABLED))
|
||||
continue;
|
||||
|
||||
if (dep->gsi && dep->direction)
|
||||
dwc3_notify_event(dwc,
|
||||
DWC3_CONTROLLER_NOTIFY_CLEAR_DB, 0);
|
||||
|
||||
dwc3_remove_requests(dwc, dep);
|
||||
}
|
||||
}
|
||||
|
||||
static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
|
||||
{
|
||||
u32 reg;
|
||||
u32 timeout = 500;
|
||||
|
||||
if (pm_runtime_suspended(dwc->dev))
|
||||
return 0;
|
||||
u32 timeout = 1500;
|
||||
|
||||
dbg_event(0xFF, "run_stop", is_on);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
if (is_on) {
|
||||
if (dwc->revision <= DWC3_REVISION_187A) {
|
||||
|
|
@ -1814,6 +1864,10 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
|
|||
|
||||
if (dwc->revision >= DWC3_REVISION_194A)
|
||||
reg &= ~DWC3_DCTL_KEEP_CONNECT;
|
||||
|
||||
dwc3_event_buffers_setup(dwc);
|
||||
__dwc3_gadget_start(dwc);
|
||||
|
||||
reg |= DWC3_DCTL_RUN_STOP;
|
||||
|
||||
if (dwc->has_hibernation)
|
||||
|
|
@ -1821,6 +1875,19 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
|
|||
|
||||
dwc->pullups_connected = true;
|
||||
} else {
|
||||
dwc3_gadget_disable_irq(dwc);
|
||||
__dwc3_gadget_ep_disable(dwc->eps[0]);
|
||||
__dwc3_gadget_ep_disable(dwc->eps[1]);
|
||||
|
||||
/*
|
||||
* According to dwc3 databook, it is must to remove any active
|
||||
* transfers before trying to stop USB device controller. Hence
|
||||
* call dwc3_stop_active_transfers() API before stopping USB
|
||||
* device controller.
|
||||
*/
|
||||
dwc3_stop_active_transfers(dwc);
|
||||
dwc3_notify_event(dwc, DWC3_GSI_EVT_BUF_CLEAR, 0);
|
||||
|
||||
reg &= ~DWC3_DCTL_RUN_STOP;
|
||||
|
||||
if (dwc->has_hibernation && !suspend)
|
||||
|
|
@ -1836,9 +1903,30 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
|
|||
reg &= DWC3_DSTS_DEVCTRLHLT;
|
||||
} while (--timeout && !(!is_on ^ !reg));
|
||||
|
||||
if (!timeout)
|
||||
return -ETIMEDOUT;
|
||||
if (!is_on)
|
||||
dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_CLEAR_DB, 0);
|
||||
|
||||
if (!timeout) {
|
||||
dev_err(dwc->dev, "failed to %s controller\n",
|
||||
is_on ? "start" : "stop");
|
||||
if (is_on)
|
||||
dbg_event(0xFF, "STARTTOUT", reg);
|
||||
else
|
||||
dbg_event(0xFF, "STOPTOUT", reg);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
|
||||
{
|
||||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
|
||||
dwc->vbus_draw = mA;
|
||||
dev_dbg(dwc->dev, "Notify controller from %s. mA = %u\n", __func__, mA);
|
||||
dbg_event(0xFF, "currentDraw", mA);
|
||||
dwc3_notify_event(dwc, DWC3_CONTROLLER_SET_CURRENT_DRAW_EVENT, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1849,6 +1937,21 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
|
|||
int ret;
|
||||
|
||||
is_on = !!is_on;
|
||||
dwc->softconnect = is_on;
|
||||
|
||||
if (((dwc->dr_mode == USB_DR_MODE_OTG) && !dwc->vbus_active)
|
||||
|| !dwc->gadget_driver) {
|
||||
/*
|
||||
* Need to wait for vbus_session(on) from otg driver or to
|
||||
* the udc_start.
|
||||
*/
|
||||
dbg_event(0xFF, "WaitPullup", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(dwc->dev);
|
||||
dbg_event(0xFF, "Pullup gsync",
|
||||
atomic_read(&dwc->dev->power.usage_count));
|
||||
|
||||
/*
|
||||
* Per databook, when we want to stop the gadget, if a control transfer
|
||||
|
|
@ -1866,9 +1969,21 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
|
|||
}
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
/*
|
||||
* If we are here after bus suspend notify otg state machine to
|
||||
* increment pm usage count of dwc to prevent pm_runtime_suspend
|
||||
* during enumeration.
|
||||
*/
|
||||
dwc->b_suspend = false;
|
||||
dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT, 0);
|
||||
|
||||
ret = dwc3_gadget_run_stop(dwc, is_on, false);
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
pm_runtime_mark_last_busy(dwc->dev);
|
||||
pm_runtime_put_autosuspend(dwc->dev);
|
||||
dbg_event(0xFF, "Pullup put",
|
||||
atomic_read(&dwc->dev->power.usage_count));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -1876,6 +1991,7 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
|
|||
{
|
||||
u32 reg;
|
||||
|
||||
dbg_event(0xFF, "UnmaskINT", 0);
|
||||
/* Enable all but Start and End of Frame IRQs */
|
||||
reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
|
||||
DWC3_DEVTEN_EVNTOVERFLOWEN |
|
||||
|
|
@ -1886,19 +2002,25 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
|
|||
DWC3_DEVTEN_USBRSTEN |
|
||||
DWC3_DEVTEN_DISCONNEVTEN);
|
||||
|
||||
if (dwc->revision < DWC3_REVISION_250A)
|
||||
/*
|
||||
* Enable SUSPENDEVENT(BIT:6) for version 230A and above
|
||||
* else enable USB Link change event (BIT:3) for older version
|
||||
*/
|
||||
if (dwc->revision < DWC3_REVISION_230A)
|
||||
reg |= DWC3_DEVTEN_ULSTCNGEN;
|
||||
else
|
||||
reg |= DWC3_DEVTEN_EOPFEN;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
|
||||
}
|
||||
|
||||
static void dwc3_gadget_disable_irq(struct dwc3 *dwc)
|
||||
void dwc3_gadget_disable_irq(struct dwc3 *dwc)
|
||||
{
|
||||
dbg_event(0xFF, "MaskINT", 0);
|
||||
/* mask all interrupts */
|
||||
dwc3_writel(dwc->regs, DWC3_DEVTEN, 0x00);
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_interrupt(int irq, void *_dwc);
|
||||
static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc);
|
||||
|
||||
/**
|
||||
|
|
@ -1942,12 +2064,59 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc)
|
|||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
}
|
||||
|
||||
static int dwc3_gadget_vbus_session(struct usb_gadget *_gadget, int is_active)
|
||||
{
|
||||
struct dwc3 *dwc = gadget_to_dwc(_gadget);
|
||||
unsigned long flags;
|
||||
|
||||
if (dwc->dr_mode != USB_DR_MODE_OTG)
|
||||
return -EPERM;
|
||||
|
||||
is_active = !!is_active;
|
||||
|
||||
dbg_event(0xFF, "VbusSess", is_active);
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
/* Mark that the vbus was powered */
|
||||
dwc->vbus_active = is_active;
|
||||
|
||||
/*
|
||||
* Check if upper level usb_gadget_driver was already registered with
|
||||
* this udc controller driver (if dwc3_gadget_start was called)
|
||||
*/
|
||||
if (dwc->gadget_driver && dwc->softconnect) {
|
||||
if (dwc->vbus_active) {
|
||||
/*
|
||||
* Both vbus was activated by otg and pullup was
|
||||
* signaled by the gadget driver.
|
||||
*/
|
||||
dwc3_gadget_run_stop(dwc, 1, false);
|
||||
} else {
|
||||
dwc3_gadget_run_stop(dwc, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clearing run/stop bit might occur before disconnect event is seen.
|
||||
* Make sure to let gadget driver know in that case.
|
||||
*/
|
||||
if (!dwc->vbus_active) {
|
||||
dev_dbg(dwc->dev, "calling disconnect from %s\n", __func__);
|
||||
dwc3_gadget_disconnect_interrupt(dwc);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __dwc3_gadget_start(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3_ep *dep;
|
||||
int ret = 0;
|
||||
u32 reg;
|
||||
|
||||
dbg_event(0xFF, "__Gadgetstart", 0);
|
||||
|
||||
/*
|
||||
* Use IMOD if enabled via dwc->imod_interval. Otherwise, if
|
||||
* the core supports IMOD, disable it.
|
||||
|
|
@ -1974,6 +2143,17 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
|
|||
|
||||
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
|
||||
|
||||
/*
|
||||
* Programs the number of outstanding pipelined transfer requests
|
||||
* the AXI master pushes to the AXI slave.
|
||||
*/
|
||||
if (dwc->revision >= DWC3_REVISION_270A) {
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG1);
|
||||
reg &= ~DWC3_GSBUSCFG1_PIPETRANSLIMIT_MASK;
|
||||
reg |= DWC3_GSBUSCFG1_PIPETRANSLIMIT(0xe);
|
||||
dwc3_writel(dwc->regs, DWC3_GSBUSCFG1, reg);
|
||||
}
|
||||
|
||||
dwc3_gadget_setup_nump(dwc);
|
||||
|
||||
/* Start with SuperSpeed Default */
|
||||
|
|
@ -2015,45 +2195,37 @@ static int dwc3_gadget_start(struct usb_gadget *g,
|
|||
struct dwc3 *dwc = gadget_to_dwc(g);
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
int irq;
|
||||
|
||||
irq = dwc->irq_gadget;
|
||||
ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt,
|
||||
IRQF_SHARED, "dwc3", dwc->ev_buf);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
|
||||
irq, ret);
|
||||
goto err0;
|
||||
}
|
||||
|
||||
dbg_event(0xFF, "Gadgetstart", 0);
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (dwc->gadget_driver) {
|
||||
dev_err(dwc->dev, "%s is already bound to %s\n",
|
||||
dwc->gadget.name,
|
||||
dwc->gadget_driver->driver.name);
|
||||
ret = -EBUSY;
|
||||
goto err1;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
dwc->gadget_driver = driver;
|
||||
|
||||
if (pm_runtime_active(dwc->dev))
|
||||
__dwc3_gadget_start(dwc);
|
||||
|
||||
/*
|
||||
* For DRD, this might get called by gadget driver during bootup
|
||||
* even though host mode might be active. Don't actually perform
|
||||
* device-specific initialization until device mode is activated.
|
||||
* In that case dwc3_gadget_restart() will handle it.
|
||||
*/
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
return 0;
|
||||
|
||||
err1:
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
free_irq(irq, dwc);
|
||||
|
||||
err0:
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __dwc3_gadget_stop(struct dwc3 *dwc)
|
||||
{
|
||||
dbg_event(0xFF, "__Gadgetstop", 0);
|
||||
dwc3_gadget_disable_irq(dwc);
|
||||
__dwc3_gadget_ep_disable(dwc->eps[0]);
|
||||
__dwc3_gadget_ep_disable(dwc->eps[1]);
|
||||
|
|
@ -2065,17 +2237,12 @@ static int dwc3_gadget_stop(struct usb_gadget *g)
|
|||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
|
||||
if (pm_runtime_suspended(dwc->dev))
|
||||
goto out;
|
||||
|
||||
__dwc3_gadget_stop(dwc);
|
||||
|
||||
out:
|
||||
dwc->gadget_driver = NULL;
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
|
||||
free_irq(dwc->irq_gadget, dwc->ev_buf);
|
||||
dbg_event(0xFF, "fwq_started", 0);
|
||||
flush_workqueue(dwc->dwc_wq);
|
||||
dbg_event(0xFF, "fwq_completed", 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2184,6 +2351,8 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
|
|||
.get_frame = dwc3_gadget_get_frame,
|
||||
.wakeup = dwc3_gadget_wakeup,
|
||||
.set_selfpowered = dwc3_gadget_set_selfpowered,
|
||||
.vbus_session = dwc3_gadget_vbus_session,
|
||||
.vbus_draw = dwc3_gadget_vbus_draw,
|
||||
.pullup = dwc3_gadget_pullup,
|
||||
.udc_start = dwc3_gadget_start,
|
||||
.udc_stop = dwc3_gadget_stop,
|
||||
|
|
@ -2193,6 +2362,10 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
|
|||
|
||||
/* -------------------------------------------------------------------------- */
|
||||
|
||||
#define NUM_GSI_OUT_EPS 1
|
||||
#define NUM_GSI_IN_EPS 2
|
||||
|
||||
|
||||
static int dwc3_gadget_init_control_endpoint(struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
|
|
@ -2321,15 +2494,39 @@ static int dwc3_gadget_init_endpoint(struct dwc3 *dwc, u8 epnum)
|
|||
static int dwc3_gadget_init_endpoints(struct dwc3 *dwc, u8 total)
|
||||
{
|
||||
u8 epnum;
|
||||
u8 out_count;
|
||||
u8 in_count;
|
||||
u8 idx;
|
||||
struct dwc3_ep *dep;
|
||||
|
||||
in_count = out_count = total / 2;
|
||||
out_count += total & 1; /* in case odd, there is one more OUT */
|
||||
|
||||
INIT_LIST_HEAD(&dwc->gadget.ep_list);
|
||||
|
||||
for (epnum = 0; epnum < total; epnum++) {
|
||||
int ret;
|
||||
u8 num = epnum >> 1;
|
||||
|
||||
ret = dwc3_gadget_init_endpoint(dwc, epnum);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dep = dwc->eps[epnum];
|
||||
/* Reserve EPs at the end for GSI */
|
||||
if (!dep->direction && num >
|
||||
out_count - NUM_GSI_OUT_EPS - 1) {
|
||||
idx = num - (out_count - NUM_GSI_OUT_EPS - 1);
|
||||
snprintf(dep->name, sizeof(dep->name), "gsi-epout%d",
|
||||
idx);
|
||||
dep->gsi = true;
|
||||
} else if (dep->direction && num >
|
||||
in_count - NUM_GSI_IN_EPS - 1) {
|
||||
idx = num - (in_count - NUM_GSI_IN_EPS - 1);
|
||||
snprintf(dep->name, sizeof(dep->name), "gsi-epin%d",
|
||||
idx);
|
||||
dep->gsi = true;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -2644,6 +2841,7 @@ static void dwc3_disconnect_gadget(struct dwc3 *dwc)
|
|||
{
|
||||
if (dwc->gadget_driver && dwc->gadget_driver->disconnect) {
|
||||
spin_unlock(&dwc->lock);
|
||||
dbg_event(0xFF, "DISCONNECT", 0);
|
||||
dwc->gadget_driver->disconnect(&dwc->gadget);
|
||||
spin_lock(&dwc->lock);
|
||||
}
|
||||
|
|
@ -2653,6 +2851,7 @@ static void dwc3_suspend_gadget(struct dwc3 *dwc)
|
|||
{
|
||||
if (dwc->gadget_driver && dwc->gadget_driver->suspend) {
|
||||
spin_unlock(&dwc->lock);
|
||||
dbg_event(0xFF, "SUSPEND", 0);
|
||||
dwc->gadget_driver->suspend(&dwc->gadget);
|
||||
spin_lock(&dwc->lock);
|
||||
}
|
||||
|
|
@ -2662,6 +2861,7 @@ static void dwc3_resume_gadget(struct dwc3 *dwc)
|
|||
{
|
||||
if (dwc->gadget_driver && dwc->gadget_driver->resume) {
|
||||
spin_unlock(&dwc->lock);
|
||||
dbg_event(0xFF, "RESUME", 0);
|
||||
dwc->gadget_driver->resume(&dwc->gadget);
|
||||
spin_lock(&dwc->lock);
|
||||
}
|
||||
|
|
@ -2674,13 +2874,13 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
|
|||
|
||||
if (dwc->gadget.speed != USB_SPEED_UNKNOWN) {
|
||||
spin_unlock(&dwc->lock);
|
||||
dbg_event(0xFF, "UDC RESET", 0);
|
||||
usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver);
|
||||
spin_lock(&dwc->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
|
||||
bool interrupt)
|
||||
void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
struct dwc3_gadget_ep_cmd_params params;
|
||||
|
|
@ -2690,6 +2890,9 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
|
|||
if (!(dep->flags & DWC3_EP_TRANSFER_STARTED))
|
||||
return;
|
||||
|
||||
dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_DISABLE_UPDXFER,
|
||||
dep->number);
|
||||
|
||||
/*
|
||||
* NOTICE: We are violating what the Databook says about the
|
||||
* EndTransfer command. Ideally we would _always_ wait for the
|
||||
|
|
@ -2730,9 +2933,12 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
|
|||
WARN_ON_ONCE(ret);
|
||||
dep->resource_index = 0;
|
||||
|
||||
dbg_log_string("%s(%d): endxfer ret:%d", dep->name, dep->number, ret);
|
||||
|
||||
if (dwc3_is_usb31(dwc) || dwc->revision < DWC3_REVISION_310A)
|
||||
udelay(100);
|
||||
}
|
||||
EXPORT_SYMBOL(dwc3_stop_active_transfer);
|
||||
|
||||
static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
|
||||
{
|
||||
|
|
@ -2752,6 +2958,7 @@ static void dwc3_clear_stall_all_ep(struct dwc3 *dwc)
|
|||
dep->flags &= ~DWC3_EP_STALL;
|
||||
|
||||
ret = dwc3_send_clear_stall_ep_cmd(dep);
|
||||
dbg_event(dep->number, "ECLRSTALL", ret);
|
||||
WARN_ON_ONCE(ret);
|
||||
}
|
||||
}
|
||||
|
|
@ -2760,6 +2967,11 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
|
|||
{
|
||||
int reg;
|
||||
|
||||
dbg_event(0xFF, "DISCONNECT INT", 0);
|
||||
dev_dbg(dwc->dev, "Notify OTG from %s\n", __func__);
|
||||
dwc->b_suspend = false;
|
||||
dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT, 0);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg &= ~DWC3_DCTL_INITU1ENA;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
|
|
@ -2771,6 +2983,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
|
|||
|
||||
dwc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
dwc->setup_packet_pending = false;
|
||||
dwc->link_state = DWC3_LINK_STATE_SS_DIS;
|
||||
usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
|
||||
|
||||
dwc->connected = false;
|
||||
|
|
@ -2813,6 +3026,13 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
|||
dwc3_gadget_disconnect_interrupt(dwc);
|
||||
}
|
||||
|
||||
dbg_event(0xFF, "BUS RESET", 0);
|
||||
dev_dbg(dwc->dev, "Notify OTG from %s\n", __func__);
|
||||
dwc->b_suspend = false;
|
||||
dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT, 0);
|
||||
|
||||
usb_gadget_vbus_draw(&dwc->gadget, 100);
|
||||
|
||||
dwc3_reset_gadget(dwc);
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
|
|
@ -2825,6 +3045,9 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
|
|||
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
|
||||
reg &= ~(DWC3_DCFG_DEVADDR_MASK);
|
||||
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
|
||||
|
||||
dwc->gadget.speed = USB_SPEED_UNKNOWN;
|
||||
dwc->link_state = DWC3_LINK_STATE_U0;
|
||||
}
|
||||
|
||||
static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
||||
|
|
@ -2834,6 +3057,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|||
u32 reg;
|
||||
u8 speed;
|
||||
|
||||
dbg_event(0xFF, "CONNECT DONE", 0);
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
speed = reg & DWC3_DSTS_CONNECTSPD;
|
||||
dwc->speed = speed;
|
||||
|
|
@ -2942,6 +3166,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|||
return;
|
||||
}
|
||||
|
||||
dwc3_notify_event(dwc, DWC3_CONTROLLER_CONNDONE_EVENT, 0);
|
||||
|
||||
/*
|
||||
* Configure PHY via GUSB3PIPECTLn if required.
|
||||
*
|
||||
|
|
@ -2953,6 +3179,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
|
|||
|
||||
static void dwc3_gadget_wakeup_interrupt(struct dwc3 *dwc)
|
||||
{
|
||||
dbg_event(0xFF, "WAKEUP", 0);
|
||||
|
||||
/*
|
||||
* TODO take core out of low power mode when that's
|
||||
* implemented.
|
||||
|
|
@ -3060,6 +3288,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
|
|||
break;
|
||||
}
|
||||
|
||||
dev_dbg(dwc->dev, "Going from (%d)--->(%d)\n", dwc->link_state, next);
|
||||
dwc->link_state = next;
|
||||
}
|
||||
|
||||
|
|
@ -3068,9 +3297,31 @@ static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc,
|
|||
{
|
||||
enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK;
|
||||
|
||||
if (dwc->link_state != next && next == DWC3_LINK_STATE_U3)
|
||||
dbg_event(0xFF, "SUSPEND INT", 0);
|
||||
dev_dbg(dwc->dev, "%s Entry to %d\n", __func__, next);
|
||||
|
||||
if (dwc->link_state != next && next == DWC3_LINK_STATE_U3) {
|
||||
/*
|
||||
* When first connecting the cable, even before the initial
|
||||
* DWC3_DEVICE_EVENT_RESET or DWC3_DEVICE_EVENT_CONNECT_DONE
|
||||
* events, the controller sees a DWC3_DEVICE_EVENT_SUSPEND
|
||||
* event. In such a case, ignore.
|
||||
* Ignore suspend event until device side usb is not into
|
||||
* CONFIGURED state.
|
||||
*/
|
||||
if (dwc->gadget.state != USB_STATE_CONFIGURED) {
|
||||
dev_err(dwc->dev, "%s(): state:%d. Ignore SUSPEND.\n",
|
||||
__func__, dwc->gadget.state);
|
||||
return;
|
||||
}
|
||||
|
||||
dwc3_suspend_gadget(dwc);
|
||||
|
||||
dev_dbg(dwc->dev, "Notify OTG from %s\n", __func__);
|
||||
dwc->b_suspend = true;
|
||||
dwc3_notify_event(dwc, DWC3_CONTROLLER_NOTIFY_OTG_EVENT, 0);
|
||||
}
|
||||
|
||||
dwc->link_state = next;
|
||||
}
|
||||
|
||||
|
|
@ -3132,6 +3383,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
|
|||
case DWC3_DEVICE_EVENT_EOPF:
|
||||
/* It changed to be suspend event for version 2.30a and above */
|
||||
if (dwc->revision >= DWC3_REVISION_230A) {
|
||||
dbg_event(0xFF, "GAD SUS", 0);
|
||||
dwc->dbg_gadget_events.suspend++;
|
||||
/*
|
||||
* Ignore suspend event until the gadget enters into
|
||||
|
|
@ -3146,7 +3398,9 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
|
|||
dwc->dbg_gadget_events.sof++;
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
|
||||
dbg_event(0xFF, "ERROR", 0);
|
||||
dwc->dbg_gadget_events.erratic_error++;
|
||||
dwc->err_evt_seen = true;
|
||||
break;
|
||||
case DWC3_DEVICE_EVENT_CMD_CMPL:
|
||||
dwc->dbg_gadget_events.cmdcmplt++;
|
||||
|
|
@ -3192,6 +3446,19 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
|
|||
|
||||
dwc3_process_event_entry(dwc, &event);
|
||||
|
||||
if (dwc->err_evt_seen) {
|
||||
/*
|
||||
* if erratic error, skip remaining events
|
||||
* while controller undergoes reset
|
||||
*/
|
||||
evt->lpos = (evt->lpos + left) %
|
||||
DWC3_EVENT_BUFFERS_SIZE;
|
||||
if (dwc3_notify_event(dwc,
|
||||
DWC3_CONTROLLER_ERROR_EVENT, 0))
|
||||
dwc->err_evt_seen = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME we wrap around correctly to the next entry as
|
||||
* almost all entries are 4 bytes in size. There is one
|
||||
|
|
@ -3223,6 +3490,15 @@ static irqreturn_t dwc3_process_event_buf(struct dwc3_event_buffer *evt)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void dwc3_bh_work(struct work_struct *w)
|
||||
{
|
||||
struct dwc3 *dwc = container_of(w, struct dwc3, bh_work);
|
||||
|
||||
pm_runtime_get_sync(dwc->dev);
|
||||
dwc3_thread_interrupt(dwc->irq, dwc->ev_buf);
|
||||
pm_runtime_put(dwc->dev);
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_thread_interrupt(int irq, void *_evt)
|
||||
{
|
||||
struct dwc3_event_buffer *evt = _evt;
|
||||
|
|
@ -3256,12 +3532,9 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
|
|||
start_time = ktime_get();
|
||||
dwc->irq_cnt++;
|
||||
|
||||
if (pm_runtime_suspended(dwc->dev)) {
|
||||
pm_runtime_get(dwc->dev);
|
||||
disable_irq_nosync(dwc->irq_gadget);
|
||||
dwc->pending_events = true;
|
||||
/* controller reset is still pending */
|
||||
if (dwc->err_evt_seen)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* With PCIe legacy interrupt, test shows that top-half irq handler can
|
||||
|
|
@ -3302,11 +3575,20 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
|
|||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t dwc3_interrupt(int irq, void *_evt)
|
||||
irqreturn_t dwc3_interrupt(int irq, void *_dwc)
|
||||
{
|
||||
struct dwc3_event_buffer *evt = _evt;
|
||||
struct dwc3 *dwc = _dwc;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
irqreturn_t status;
|
||||
|
||||
return dwc3_check_event_buf(evt);
|
||||
status = dwc3_check_event_buf(dwc->ev_buf);
|
||||
if (status == IRQ_WAKE_THREAD)
|
||||
ret = status;
|
||||
|
||||
if (ret == IRQ_WAKE_THREAD)
|
||||
queue_work(dwc->dwc_wq, &dwc->bh_work);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int dwc3_gadget_get_irq(struct dwc3 *dwc)
|
||||
|
|
|
|||
|
|
@ -58,6 +58,9 @@ struct dwc3;
|
|||
|
||||
#define to_dwc3_request(r) (container_of(r, struct dwc3_request, request))
|
||||
|
||||
irqreturn_t dwc3_interrupt(int irq, void *_dwc);
|
||||
void dwc3_bh_work(struct work_struct *w);
|
||||
|
||||
/**
|
||||
* next_request - gets the next request on the given list
|
||||
* @list: the request list to operate on
|
||||
|
|
@ -111,6 +114,15 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
|
|||
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
||||
gfp_t gfp_flags);
|
||||
int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
|
||||
void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt);
|
||||
|
||||
static inline dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep,
|
||||
struct dwc3_trb *trb)
|
||||
{
|
||||
u32 offset = (char *) trb - (char *) dep->trb_pool;
|
||||
|
||||
return dep->trb_pool_dma + offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
|
||||
|
|
|
|||
|
|
@ -134,8 +134,11 @@ err:
|
|||
platform_device_put(xhci);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(dwc3_host_init);
|
||||
|
||||
void dwc3_host_exit(struct dwc3 *dwc)
|
||||
{
|
||||
platform_device_unregister(dwc->xhci);
|
||||
}
|
||||
EXPORT_SYMBOL(dwc3_host_exit);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue