usb: dwc3: Add QTI MSM platform specific feature and other changes
This change adds the following QTI MSM platform specific features to the USB DWC3 core & gadget drivers: - BAM endpoint support - GSI endpoint support - low power mode / remote wakeup - function suspend/wake - glue layer notifications - high speed-only fallback - add additional debug logging Change-Id: I0f733d05f3f88a4a734d8489b5eb548391ab3af3 Signed-off-by: Mayank Rana <mrana@codeaurora.org> Signed-off-by: Jack Pham <jackp@codeaurora.org> Signed-off-by: Hemant Kumar <hemantk@codeaurora.org>
This commit is contained in:
parent
5a5e9243e0
commit
6252350019
14 changed files with 1239 additions and 262 deletions
Documentation/devicetree/bindings/usb
drivers/usb
include/linux/usb
|
@ -97,6 +97,8 @@ Optional properties:
|
|||
- snps,xhci-imod-value: Interrupt moderation interval for host mode
|
||||
(in increments of 250nsec).
|
||||
- usb-core-id: Differentiates between different controllers present on a device.
|
||||
- snps,bus-suspend-enable: If present then controller supports low power mode
|
||||
during bus suspend.
|
||||
|
||||
- <DEPRECATED> tx-fifo-resize: determines if the FIFO *has* to be reallocated.
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
# define_trace.h needs to know how to find our header
|
||||
CFLAGS_trace.o := -I$(src)
|
||||
CFLAGS_dwc3-msm.o := -Idrivers/usb/host -Idrivers/base/power
|
||||
|
||||
obj-$(CONFIG_USB_DWC3) += dwc3.o
|
||||
|
||||
|
|
|
@ -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,28 @@
|
|||
|
||||
#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);
|
||||
static void __dwc3_set_mode(struct dwc3 *dwc);
|
||||
|
||||
void dwc3_usb3_phy_suspend(struct dwc3 *dwc, int suspend)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
|
||||
|
||||
if (suspend)
|
||||
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
|
||||
else
|
||||
reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
|
||||
|
||||
dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* dwc3_get_dr_mode - Validates and sets dr_mode
|
||||
* @dwc: pointer to our context structure
|
||||
|
@ -106,12 +124,14 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
|
|||
dwc->current_dr_role = mode;
|
||||
}
|
||||
|
||||
static void __dwc3_set_mode(struct work_struct *work)
|
||||
static void __maybe_unused __dwc3_set_mode(struct dwc3 *dwc)
|
||||
{
|
||||
struct dwc3 *dwc = work_to_dwc(work);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
dev_dbg(dwc->dev, "%s(): desired_dr_role:%d curent_dr_role:%d\n",
|
||||
__func__, dwc->desired_dr_role, dwc->current_dr_role);
|
||||
|
||||
if (dwc->dr_mode != USB_DR_MODE_OTG)
|
||||
return;
|
||||
|
||||
|
@ -121,8 +141,10 @@ static void __dwc3_set_mode(struct work_struct *work)
|
|||
if (!dwc->desired_dr_role)
|
||||
return;
|
||||
|
||||
if (dwc->desired_dr_role == dwc->current_dr_role)
|
||||
if (dwc->desired_dr_role == dwc->current_dr_role) {
|
||||
dwc3_set_prtcap(dwc, dwc->desired_dr_role);
|
||||
return;
|
||||
}
|
||||
|
||||
if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
|
||||
return;
|
||||
|
@ -222,8 +244,36 @@ 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);
|
||||
/* Reset PHYs */
|
||||
usb_phy_reset(dwc->usb2_phy);
|
||||
|
||||
if (dwc->maximum_speed == USB_SPEED_SUPER)
|
||||
usb_phy_reset(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_HIGH)
|
||||
goto generic_phy_init;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
generic_phy_init:
|
||||
ret = phy_init(dwc->usb2_generic_phy);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -355,6 +405,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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -376,6 +429,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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -399,6 +454,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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -415,6 +472,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);
|
||||
}
|
||||
|
||||
static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc)
|
||||
|
@ -787,7 +847,7 @@ static int dwc3_core_ulpi_init(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;
|
||||
|
@ -798,6 +858,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
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.
|
||||
|
@ -844,7 +911,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
dwc3_frame_length_adjustment(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;
|
||||
|
@ -853,12 +922,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
|
||||
|
@ -923,10 +986,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
dwc3_check_params(dwc);
|
||||
|
||||
err4:
|
||||
phy_power_off(dwc->usb3_generic_phy);
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
phy_power_off(dwc->usb2_generic_phy);
|
||||
|
@ -934,6 +996,7 @@ static int dwc3_core_init(struct dwc3 *dwc)
|
|||
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);
|
||||
|
@ -1015,61 +1078,7 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dwc3_core_init_mode(struct dwc3 *dwc)
|
||||
{
|
||||
struct device *dev = dwc->dev;
|
||||
int ret;
|
||||
|
||||
switch (dwc->dr_mode) {
|
||||
case USB_DR_MODE_PERIPHERAL:
|
||||
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
|
||||
|
||||
if (dwc->usb2_phy)
|
||||
otg_set_vbus(dwc->usb2_phy->otg, false);
|
||||
phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
|
||||
phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE);
|
||||
|
||||
ret = dwc3_gadget_init(dwc);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to initialize gadget\n");
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case USB_DR_MODE_HOST:
|
||||
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_HOST);
|
||||
|
||||
if (dwc->usb2_phy)
|
||||
otg_set_vbus(dwc->usb2_phy->otg, true);
|
||||
phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
|
||||
phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
|
||||
|
||||
ret = dwc3_host_init(dwc);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to initialize host\n");
|
||||
return ret;
|
||||
}
|
||||
phy_calibrate(dwc->usb2_generic_phy);
|
||||
break;
|
||||
case USB_DR_MODE_OTG:
|
||||
INIT_WORK(&dwc->drd_work, __dwc3_set_mode);
|
||||
ret = dwc3_drd_init(dwc);
|
||||
if (ret) {
|
||||
if (ret != -EPROBE_DEFER)
|
||||
dev_err(dev, "failed to initialize dual-role\n");
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "Unsupported mode of operation %d\n", dwc->dr_mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
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:
|
||||
|
@ -1087,6 +1096,26 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
|
|||
}
|
||||
}
|
||||
|
||||
static void (*notify_event)(struct dwc3 *, unsigned int);
|
||||
void dwc3_set_notifier(void (*notify)(struct dwc3 *, unsigned int))
|
||||
{
|
||||
notify_event = notify;
|
||||
}
|
||||
EXPORT_SYMBOL(dwc3_set_notifier);
|
||||
|
||||
int dwc3_notify_event(struct dwc3 *dwc, unsigned int event)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (notify_event)
|
||||
notify_event(dwc, event);
|
||||
else
|
||||
ret = -ENODEV;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(dwc3_notify_event);
|
||||
|
||||
static void dwc3_get_properties(struct dwc3 *dwc)
|
||||
{
|
||||
struct device *dev = dwc->dev;
|
||||
|
@ -1111,6 +1140,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);
|
||||
|
||||
|
@ -1186,6 +1216,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");
|
||||
|
@ -1272,6 +1304,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)
|
||||
|
@ -1283,7 +1323,6 @@ 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");
|
||||
|
@ -1296,6 +1335,21 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
dwc->xhci_resources[0].flags = res->flags;
|
||||
dwc->xhci_resources[0].name = res->name;
|
||||
|
||||
res->start += DWC3_GLOBALS_REGS_START;
|
||||
|
||||
irq = platform_get_irq(to_platform_device(dwc->dev), 0);
|
||||
|
||||
/* will be enabled in dwc3_msm_resume() */
|
||||
irq_set_status_flags(irq, IRQ_NOAUTOEN);
|
||||
ret = devm_request_irq(dev, irq, dwc3_interrupt, IRQF_SHARED, "dwc3",
|
||||
dwc);
|
||||
if (ret) {
|
||||
dev_err(dwc->dev, "failed to request irq #%d --> %d\n",
|
||||
irq, ret);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dwc->irq = irq;
|
||||
/*
|
||||
* Request memory region but exclude xHCI regs,
|
||||
* since it will be requested by the xhci-plat driver.
|
||||
|
@ -1307,6 +1361,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);
|
||||
|
||||
|
@ -1314,7 +1376,7 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
|
||||
dwc->reset = devm_reset_control_get_optional_shared(dev, NULL);
|
||||
if (IS_ERR(dwc->reset))
|
||||
return PTR_ERR(dwc->reset);
|
||||
goto skip_clk_reset;
|
||||
|
||||
if (dev->of_node) {
|
||||
dwc->num_clks = ARRAY_SIZE(dwc3_core_clks);
|
||||
|
@ -1342,47 +1404,32 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto unprepare_clks;
|
||||
|
||||
skip_clk_reset:
|
||||
platform_set_drvdata(pdev, dwc);
|
||||
dwc3_cache_hwparams(dwc);
|
||||
|
||||
init_waitqueue_head(&dwc->wait_linkstate);
|
||||
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;
|
||||
|
||||
ret = dwc3_core_init(dwc);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to initialize core\n");
|
||||
goto err4;
|
||||
}
|
||||
|
||||
dwc3_check_params(dwc);
|
||||
|
||||
ret = dwc3_core_init_mode(dwc);
|
||||
if (ret)
|
||||
goto err5;
|
||||
goto err2;
|
||||
|
||||
dwc3_debugfs_init(dwc);
|
||||
|
||||
|
@ -1395,33 +1442,29 @@ 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);
|
||||
|
||||
err4:
|
||||
dwc3_free_scratch_buffers(dwc);
|
||||
|
||||
err3:
|
||||
dwc3_free_event_buffers(dwc);
|
||||
|
||||
err2:
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
|
||||
dwc3_free_event_buffers(dwc);
|
||||
err1:
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
clk_bulk_disable(dwc->num_clks, dwc->clks);
|
||||
if (dwc->num_clks) {
|
||||
clk_bulk_disable(dwc->num_clks, dwc->clks);
|
||||
unprepare_clks:
|
||||
clk_bulk_unprepare(dwc->num_clks, dwc->clks);
|
||||
clk_bulk_unprepare(dwc->num_clks, dwc->clks);
|
||||
assert_reset:
|
||||
reset_control_assert(dwc->reset);
|
||||
reset_control_assert(dwc->reset);
|
||||
put_clks:
|
||||
clk_bulk_put(dwc->num_clks, dwc->clks);
|
||||
clk_bulk_put(dwc->num_clks, dwc->clks);
|
||||
}
|
||||
destroy_workqueue(dwc->dwc_wq);
|
||||
err0:
|
||||
/*
|
||||
* 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
|
||||
* memory region the next time probe is called.
|
||||
*/
|
||||
res->start -= DWC3_GLOBALS_REGS_START;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1429,16 +1472,17 @@ static int dwc3_probe(struct platform_device *pdev)
|
|||
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
|
||||
* memory region the next time probe is called.
|
||||
*/
|
||||
res->start -= DWC3_GLOBALS_REGS_START;
|
||||
|
||||
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);
|
||||
|
||||
|
@ -1630,6 +1674,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))
|
||||
return 0;
|
||||
|
||||
if (dwc3_runtime_checks(dwc))
|
||||
return -EBUSY;
|
||||
|
||||
|
@ -1647,6 +1695,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))
|
||||
return 0;
|
||||
|
||||
device_init_wakeup(dev, false);
|
||||
|
||||
ret = dwc3_resume_common(dwc, PMSG_AUTO_RESUME);
|
||||
|
@ -1696,6 +1748,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))
|
||||
return 0;
|
||||
|
||||
ret = dwc3_suspend_common(dwc, PMSG_SUSPEND);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -1710,6 +1766,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))
|
||||
return 0;
|
||||
|
||||
pinctrl_pm_select_default_state(dev);
|
||||
|
||||
ret = dwc3_resume_common(dwc, PMSG_RESUME);
|
||||
|
|
|
@ -161,8 +161,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 Debug Queue/FIFO Space Available Register */
|
||||
#define DWC3_GDBGFIFOSPACE_NUM(n) ((n) & 0x1f)
|
||||
#define DWC3_GDBGFIFOSPACE_TYPE(n) (((n) << 5) & 0x1e0)
|
||||
|
@ -240,6 +252,9 @@
|
|||
#define DWC3_GSTS_CSR_TIMEOUT BIT(5)
|
||||
#define DWC3_GSTS_BUS_ERR_ADDR_VLD BIT(4)
|
||||
|
||||
/* 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)
|
||||
|
@ -278,6 +293,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 */
|
||||
|
@ -458,6 +474,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
|
||||
|
@ -659,10 +676,13 @@ struct dwc3_ep_events {
|
|||
* @wait_end_transfer: wait_queue_head_t for waiting on End Transfer complete
|
||||
* @lock: spinlock for endpoint request queue traversal
|
||||
* @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, ...)
|
||||
|
@ -688,8 +708,10 @@ struct dwc3_ep {
|
|||
spinlock_t lock;
|
||||
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;
|
||||
|
@ -906,7 +928,23 @@ struct dwc3_scratchpad_array {
|
|||
__le64 dma_adr[DWC3_MAX_HIBER_SCRATCHBUFS];
|
||||
};
|
||||
|
||||
#define MAX_INTR_STATS 10
|
||||
#define DWC3_CONTROLLER_ERROR_EVENT 0
|
||||
#define DWC3_CONTROLLER_RESET_EVENT 1
|
||||
#define DWC3_CONTROLLER_POST_RESET_EVENT 2
|
||||
#define DWC3_CORE_PM_SUSPEND_EVENT 3
|
||||
#define DWC3_CORE_PM_RESUME_EVENT 4
|
||||
#define DWC3_CONTROLLER_CONNDONE_EVENT 5
|
||||
#define DWC3_CONTROLLER_NOTIFY_OTG_EVENT 6
|
||||
#define DWC3_CONTROLLER_SET_CURRENT_DRAW_EVENT 7
|
||||
#define DWC3_CONTROLLER_RESTART_USB_SESSION 8
|
||||
|
||||
/* USB GSI event buffer related notification */
|
||||
#define DWC3_GSI_EVT_BUF_ALLOC 9
|
||||
#define DWC3_GSI_EVT_BUF_SETUP 10
|
||||
#define DWC3_GSI_EVT_BUF_CLEANUP 11
|
||||
#define DWC3_GSI_EVT_BUF_FREE 12
|
||||
|
||||
#define MAX_INTR_STATS 10
|
||||
|
||||
/**
|
||||
* struct dwc3 - representation of our controller
|
||||
|
@ -940,9 +978,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
|
||||
* @dr_mode: requested mode of operation
|
||||
* @current_dr_role: current role of operation when in dual-role mode
|
||||
|
@ -1024,6 +1064,15 @@ 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
|
||||
* @wait_linkstate: waitqueue for waiting LINK to move into required state
|
||||
* @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
|
||||
|
@ -1080,6 +1129,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;
|
||||
|
@ -1097,6 +1147,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
|
||||
|
@ -1166,6 +1217,8 @@ struct dwc3 {
|
|||
|
||||
const char *hsphy_interface;
|
||||
|
||||
struct work_struct wakeup_work;
|
||||
|
||||
unsigned connected:1;
|
||||
unsigned delayed_status:1;
|
||||
unsigned ep0_bounced:1;
|
||||
|
@ -1199,25 +1252,45 @@ 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;
|
||||
int core_id;
|
||||
struct workqueue_struct *dwc_wq;
|
||||
struct work_struct bh_work;
|
||||
|
||||
unsigned long ep_cmd_timeout_cnt;
|
||||
unsigned long l1_remote_wakeup_cnt;
|
||||
|
||||
wait_queue_head_t wait_linkstate;
|
||||
|
||||
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 work_to_dwc(w) (container_of((w), struct dwc3, drd_work))
|
||||
|
@ -1415,11 +1488,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)
|
||||
|
@ -1434,6 +1512,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)
|
||||
|
@ -1489,4 +1573,8 @@ static inline void dwc3_ulpi_exit(struct dwc3 *dwc)
|
|||
{ }
|
||||
#endif
|
||||
|
||||
extern void dwc3_set_notifier(
|
||||
void (*notify)(struct dwc3 *dwc3, unsigned int event));
|
||||
extern int dwc3_notify_event(struct dwc3 *dwc3, unsigned int event);
|
||||
void dwc3_usb3_phy_suspend(struct dwc3 *dwc, int suspend);
|
||||
#endif /* __DRIVERS_USB_DWC3_CORE_H */
|
||||
|
|
|
@ -288,6 +288,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);
|
||||
|
@ -320,7 +325,12 @@ static ssize_t dwc3_mode_write(struct file *file,
|
|||
struct seq_file *s = file->private_data;
|
||||
struct dwc3 *dwc = s->private;
|
||||
u32 mode = 0;
|
||||
char buf[32];
|
||||
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;
|
||||
|
@ -353,6 +363,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;
|
||||
|
@ -399,6 +414,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;
|
||||
|
||||
|
@ -437,6 +457,11 @@ static int dwc3_link_state_show(struct seq_file *s, void *unused)
|
|||
enum dwc3_link_state state;
|
||||
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_DSTS);
|
||||
state = DWC3_DSTS_USBLNKST(reg);
|
||||
|
@ -461,6 +486,11 @@ static ssize_t dwc3_link_state_write(struct file *file,
|
|||
enum dwc3_link_state state = 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;
|
||||
|
||||
|
@ -506,6 +536,11 @@ static int dwc3_tx_fifo_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_TXFIFOQ);
|
||||
seq_printf(s, "%u\n", val);
|
||||
|
@ -521,6 +556,11 @@ static int dwc3_rx_fifo_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_RXFIFOQ);
|
||||
seq_printf(s, "%u\n", val);
|
||||
|
@ -551,6 +591,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);
|
||||
|
@ -566,6 +611,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);
|
||||
|
@ -581,6 +631,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);
|
||||
|
@ -596,6 +651,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);
|
||||
|
@ -887,6 +947,12 @@ 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, "l1_remote_wakeup_cnt : %lu\n",
|
||||
dwc->l1_remote_wakeup_cnt);
|
||||
|
||||
spin_unlock_irqrestore(&dwc->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -662,8 +662,7 @@ static int __dwc3_msm_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
|
|||
list_del(&req->list);
|
||||
return ret;
|
||||
}
|
||||
dep->flags |= DWC3_EP_BUSY;
|
||||
dep->resource_index = dwc3_gadget_ep_get_transfer_index(dep);
|
||||
dwc3_gadget_ep_get_transfer_index(dep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -907,7 +906,7 @@ static int gsi_startxfer_for_ep(struct usb_ep *ep)
|
|||
|
||||
if (ret < 0)
|
||||
dev_dbg(dwc->dev, "Fail StrtXfr on GSI EP#%d\n", dep->number);
|
||||
dep->resource_index = dwc3_gadget_ep_get_transfer_index(dep);
|
||||
dwc3_gadget_ep_get_transfer_index(dep);
|
||||
dev_dbg(dwc->dev, "XferRsc = %x", dep->resource_index);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1019,7 +1018,6 @@ static int gsi_updatexfer_for_ep(struct usb_ep *ep,
|
|||
cmd = DWC3_DEPCMD_UPDATETRANSFER;
|
||||
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
|
||||
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
|
||||
dep->flags |= DWC3_EP_BUSY;
|
||||
if (ret < 0)
|
||||
dev_dbg(dwc->dev, "UpdateXfr fail on GSI EP#%d\n", dep->number);
|
||||
return ret;
|
||||
|
|
|
@ -195,6 +195,8 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
|||
unsigned long flags;
|
||||
|
||||
int ret;
|
||||
enum dwc3_link_state link_state;
|
||||
u32 reg;
|
||||
|
||||
spin_lock_irqsave(&dwc->lock, flags);
|
||||
if (!dep->endpoint.desc) {
|
||||
|
@ -210,6 +212,18 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
|
|||
goto out;
|
||||
}
|
||||
|
||||
/* if link stats is in L1 initiate remote wakeup before queuing req */
|
||||
if (dwc->speed != DWC3_DSTS_SUPERSPEED) {
|
||||
link_state = dwc3_get_link_state(dwc);
|
||||
/* in HS this link state is same as L1 */
|
||||
if (link_state == DWC3_LINK_STATE_U2) {
|
||||
dwc->l1_remote_wakeup_cnt++;
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
|
||||
reg |= DWC3_DCTL_ULSTCHNG_RECOVERY;
|
||||
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
|
||||
}
|
||||
}
|
||||
|
||||
ret = __dwc3_gadget_ep0_queue(dep, req);
|
||||
|
||||
out:
|
||||
|
@ -248,6 +262,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 +293,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)
|
||||
|
@ -334,6 +350,9 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
|
|||
usb_status |= 1 << USB_DEV_STAT_U1_ENABLED;
|
||||
if (reg & DWC3_DCTL_INITU2ENA)
|
||||
usb_status |= 1 << USB_DEV_STAT_U2_ENABLED;
|
||||
} else {
|
||||
usb_status |= dwc->gadget.remote_wakeup <<
|
||||
USB_DEVICE_REMOTE_WAKEUP;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -450,6 +469,9 @@ static int dwc3_ep0_handle_device(struct dwc3 *dwc,
|
|||
|
||||
switch (wValue) {
|
||||
case USB_DEVICE_REMOTE_WAKEUP:
|
||||
pr_debug("%s(): remote wakeup :%s\n", __func__,
|
||||
(set ? "enabled" : "disabled"));
|
||||
dwc->gadget.remote_wakeup = set;
|
||||
break;
|
||||
/*
|
||||
* 9.4.1 says only only for SS, in AddressState only for
|
||||
|
@ -684,7 +706,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)
|
||||
|
@ -796,6 +819,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
|
||||
|
@ -805,8 +829,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,
|
||||
|
@ -835,10 +867,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;
|
||||
|
@ -861,10 +890,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,
|
||||
|
@ -893,6 +925,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;
|
||||
}
|
||||
|
@ -902,6 +935,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);
|
||||
}
|
||||
|
@ -1008,6 +1042,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)
|
||||
|
@ -1024,7 +1059,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,
|
||||
|
@ -1049,7 +1088,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;
|
||||
}
|
||||
|
||||
|
@ -1080,6 +1123,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;
|
||||
}
|
||||
|
@ -1096,7 +1140,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
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -52,6 +52,8 @@ 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
|
||||
|
@ -79,6 +81,14 @@ static inline void dwc3_gadget_move_started_request(struct dwc3_request *req)
|
|||
list_move_tail(&req->list, &dep->started_list);
|
||||
}
|
||||
|
||||
static inline enum dwc3_link_state dwc3_get_link_state(struct dwc3 *dwc)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
return DWC3_DSTS_USBLNKST(reg);
|
||||
}
|
||||
|
||||
void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
|
||||
int status);
|
||||
|
||||
|
@ -90,6 +100,17 @@ 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 *dwc, u32 epnum, bool force);
|
||||
void dwc3_ep_inc_enq(struct dwc3_ep *dep);
|
||||
void dwc3_ep_inc_deq(struct dwc3_ep *dep);
|
||||
|
||||
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
|
||||
|
|
|
@ -205,3 +205,41 @@ void usb_ep_autoconfig_reset (struct usb_gadget *gadget)
|
|||
gadget->out_epnum = 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_ep_autoconfig_reset);
|
||||
|
||||
/**
|
||||
* usb_ep_autoconfig_by_name - Used to pick the endpoint by name. eg ep1in-gsi
|
||||
* @gadget: The device to which the endpoint must belong.
|
||||
* @desc: Endpoint descriptor, with endpoint direction and transfer mode
|
||||
* initialized.
|
||||
* @ep_name: EP name that is to be searched.
|
||||
*
|
||||
*/
|
||||
struct usb_ep *usb_ep_autoconfig_by_name(
|
||||
struct usb_gadget *gadget,
|
||||
struct usb_endpoint_descriptor *desc,
|
||||
const char *ep_name
|
||||
)
|
||||
{
|
||||
struct usb_ep *ep;
|
||||
bool ep_found = false;
|
||||
|
||||
list_for_each_entry(ep, &gadget->ep_list, ep_list)
|
||||
if (strcmp(ep->name, ep_name) == 0 && !ep->driver_data) {
|
||||
ep_found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ep_found) {
|
||||
desc->bEndpointAddress &= USB_DIR_IN;
|
||||
desc->bEndpointAddress |= ep->ep_num;
|
||||
ep->address = desc->bEndpointAddress;
|
||||
pr_debug("Allocating ep address:%x\n", ep->address);
|
||||
ep->desc = NULL;
|
||||
ep->comp_desc = NULL;
|
||||
return ep;
|
||||
}
|
||||
|
||||
pr_err("%s:error finding ep %s\n", __func__, ep_name);
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(usb_ep_autoconfig_by_name);
|
||||
|
|
|
@ -477,6 +477,22 @@ int usb_gadget_wakeup(struct usb_gadget *gadget)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(usb_gadget_wakeup);
|
||||
|
||||
/**
|
||||
* usb_gsi_ep_op - performs operation on GSI accelerated EP based on EP op code
|
||||
*
|
||||
* Operations such as EP configuration, TRB allocation, StartXfer etc.
|
||||
* See gsi_ep_op for more details.
|
||||
*/
|
||||
int usb_gsi_ep_op(struct usb_ep *ep,
|
||||
struct usb_gsi_request *req, enum gsi_ep_op op)
|
||||
{
|
||||
if (ep->ops->gsi_ep_op)
|
||||
return ep->ops->gsi_ep_op(ep, req, op);
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL(usb_gsi_ep_op);
|
||||
|
||||
/**
|
||||
* usb_gadget_func_wakeup - send a function remote wakeup up notification
|
||||
* to the host connected to this gadget
|
||||
|
|
|
@ -42,6 +42,10 @@
|
|||
#include <linux/log2.h>
|
||||
#include <linux/configfs.h>
|
||||
|
||||
/* FUNCTION_SUSPEND: suspend options from usb 3.0 spec Table 9-7 */
|
||||
#define FUNC_SUSPEND_OPT_SUSP_MASK BIT(0)
|
||||
#define FUNC_SUSPEND_OPT_RW_EN_MASK BIT(1)
|
||||
|
||||
/*
|
||||
* USB function drivers should return USB_GADGET_DELAYED_STATUS if they
|
||||
* wish to delay the data/status stages of the control transfer till they
|
||||
|
|
|
@ -29,8 +29,103 @@
|
|||
|
||||
#define UDC_TRACE_STR_MAX 512
|
||||
|
||||
/*
|
||||
* The following are bit fields describing the usb_request.udc_priv word.
|
||||
* These bit fields are set by function drivers that wish to queue
|
||||
* usb_requests with sps/bam parameters.
|
||||
*/
|
||||
#define MSM_PIPE_ID_MASK (0x1F)
|
||||
#define MSM_TX_PIPE_ID_OFS (16)
|
||||
#define MSM_SPS_MODE BIT(5)
|
||||
#define MSM_IS_FINITE_TRANSFER BIT(6)
|
||||
#define MSM_PRODUCER BIT(7)
|
||||
#define MSM_DISABLE_WB BIT(8)
|
||||
#define MSM_ETD_IOC BIT(9)
|
||||
#define MSM_INTERNAL_MEM BIT(10)
|
||||
#define MSM_VENDOR_ID BIT(16)
|
||||
|
||||
struct usb_ep;
|
||||
|
||||
enum ep_type {
|
||||
EP_TYPE_NORMAL = 0,
|
||||
EP_TYPE_GSI,
|
||||
};
|
||||
|
||||
/* Operations codes for GSI enabled EPs */
|
||||
enum gsi_ep_op {
|
||||
GSI_EP_OP_CONFIG = 0,
|
||||
GSI_EP_OP_STARTXFER,
|
||||
GSI_EP_OP_STORE_DBL_INFO,
|
||||
GSI_EP_OP_ENABLE_GSI,
|
||||
GSI_EP_OP_UPDATEXFER,
|
||||
GSI_EP_OP_RING_DB,
|
||||
GSI_EP_OP_ENDXFER,
|
||||
GSI_EP_OP_GET_CH_INFO,
|
||||
GSI_EP_OP_GET_XFER_IDX,
|
||||
GSI_EP_OP_PREPARE_TRBS,
|
||||
GSI_EP_OP_FREE_TRBS,
|
||||
GSI_EP_OP_SET_CLR_BLOCK_DBL,
|
||||
GSI_EP_OP_CHECK_FOR_SUSPEND,
|
||||
GSI_EP_OP_DISABLE,
|
||||
};
|
||||
|
||||
/*
|
||||
* @buf_base_addr: Base pointer to buffer allocated for each GSI enabled EP.
|
||||
* TRBs point to buffers that are split from this pool. The size of the
|
||||
* buffer is num_bufs times buf_len. num_bufs and buf_len are determined
|
||||
based on desired performance and aggregation size.
|
||||
* @dma: DMA address corresponding to buf_base_addr.
|
||||
* @num_bufs: Number of buffers associated with the GSI enabled EP. This
|
||||
* corresponds to the number of non-zlp TRBs allocated for the EP.
|
||||
* The value is determined based on desired performance for the EP.
|
||||
* @buf_len: Size of each individual buffer is determined based on aggregation
|
||||
* negotiated as per the protocol. In case of no aggregation supported by
|
||||
* the protocol, we use default values.
|
||||
* @db_reg_phs_addr_lsb: IPA channel doorbell register's physical address LSB
|
||||
* @mapped_db_reg_phs_addr_lsb: doorbell LSB IOVA address mapped with IOMMU
|
||||
* @db_reg_phs_addr_msb: IPA channel doorbell register's physical address MSB
|
||||
*/
|
||||
struct usb_gsi_request {
|
||||
void *buf_base_addr;
|
||||
dma_addr_t dma;
|
||||
size_t num_bufs;
|
||||
size_t buf_len;
|
||||
u32 db_reg_phs_addr_lsb;
|
||||
dma_addr_t mapped_db_reg_phs_addr_lsb;
|
||||
u32 db_reg_phs_addr_msb;
|
||||
struct sg_table sgt_trb_xfer_ring;
|
||||
struct sg_table sgt_data_buff;
|
||||
};
|
||||
|
||||
/*
|
||||
* @last_trb_addr: Address (LSB - based on alignment restrictions) of
|
||||
* last TRB in queue. Used to identify rollover case.
|
||||
* @const_buffer_size: TRB buffer size in KB (similar to IPA aggregation
|
||||
* configuration). Must be aligned to Max USB Packet Size.
|
||||
* Should be 1 <= const_buffer_size <= 31.
|
||||
* @depcmd_low_addr: Used by GSI hardware to write "Update Transfer" cmd
|
||||
* @depcmd_hi_addr: Used to write "Update Transfer" command.
|
||||
* @gevntcount_low_addr: GEVNCOUNT low address for GSI hardware to read and
|
||||
* clear processed events.
|
||||
* @gevntcount_hi_addr: GEVNCOUNT high address.
|
||||
* @xfer_ring_len: length of transfer ring in bytes (must be integral
|
||||
* multiple of TRB size - 16B for xDCI).
|
||||
* @xfer_ring_base_addr: physical base address of transfer ring. Address must
|
||||
* be aligned to xfer_ring_len rounded to power of two.
|
||||
* @ch_req: Used to pass request specific info for certain operations on GSI EP
|
||||
*/
|
||||
struct gsi_channel_info {
|
||||
u16 last_trb_addr;
|
||||
u8 const_buffer_size;
|
||||
u32 depcmd_low_addr;
|
||||
u8 depcmd_hi_addr;
|
||||
u32 gevntcount_low_addr;
|
||||
u8 gevntcount_hi_addr;
|
||||
u16 xfer_ring_len;
|
||||
u64 xfer_ring_base_addr;
|
||||
struct usb_gsi_request *ch_req;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct usb_request - describes one i/o request
|
||||
* @buf: Buffer used for data. Always provide this; some controllers
|
||||
|
@ -74,6 +169,7 @@ struct usb_ep;
|
|||
* Note that for writes (IN transfers) some data bytes may still
|
||||
* reside in a device-side FIFO when the request is reported as
|
||||
* complete.
|
||||
* @udc_priv: Vendor private data in usage by the UDC.
|
||||
*
|
||||
* These are allocated/freed through the endpoint they're used with. The
|
||||
* hardware's driver can add extra per-request data to the memory it returns,
|
||||
|
@ -115,6 +211,7 @@ struct usb_request {
|
|||
|
||||
int status;
|
||||
unsigned actual;
|
||||
unsigned int udc_priv;
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -145,6 +242,9 @@ struct usb_ep_ops {
|
|||
|
||||
int (*fifo_status) (struct usb_ep *ep);
|
||||
void (*fifo_flush) (struct usb_ep *ep);
|
||||
int (*gsi_ep_op)(struct usb_ep *ep, void *op_data,
|
||||
enum gsi_ep_op op);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -210,6 +310,11 @@ struct usb_ep_caps {
|
|||
* enabled and remains valid until the endpoint is disabled.
|
||||
* @comp_desc: In case of SuperSpeed support, this is the endpoint companion
|
||||
* descriptor that is used to configure the endpoint
|
||||
* @ep_type: Used to specify type of EP eg. normal vs h/w accelerated.
|
||||
* @ep_num: Used EP number
|
||||
* @ep_intr_num: Interrupter number for EP.
|
||||
* @endless: In case where endless transfer is being initiated, this is set
|
||||
* to disable usb event interrupt for few events.
|
||||
*
|
||||
* the bus controller driver lists all the general purpose endpoints in
|
||||
* gadget->ep_list. the control endpoint (gadget->ep0) is not in that list,
|
||||
|
@ -233,6 +338,10 @@ struct usb_ep {
|
|||
u8 address;
|
||||
const struct usb_endpoint_descriptor *desc;
|
||||
const struct usb_ss_ep_comp_descriptor *comp_desc;
|
||||
enum ep_type ep_type;
|
||||
u8 ep_num;
|
||||
u8 ep_intr_num;
|
||||
bool endless;
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -250,6 +359,8 @@ int usb_ep_clear_halt(struct usb_ep *ep);
|
|||
int usb_ep_set_wedge(struct usb_ep *ep);
|
||||
int usb_ep_fifo_status(struct usb_ep *ep);
|
||||
void usb_ep_fifo_flush(struct usb_ep *ep);
|
||||
int usb_gsi_ep_op(struct usb_ep *ep,
|
||||
struct usb_gsi_request *req, enum gsi_ep_op op);
|
||||
#else
|
||||
static inline void usb_ep_set_maxpacket_limit(struct usb_ep *ep,
|
||||
unsigned maxpacket_limit)
|
||||
|
@ -279,6 +390,10 @@ static inline int usb_ep_fifo_status(struct usb_ep *ep)
|
|||
{ return 0; }
|
||||
static inline void usb_ep_fifo_flush(struct usb_ep *ep)
|
||||
{ }
|
||||
|
||||
static int usb_gsi_ep_op(struct usb_ep *ep,
|
||||
struct usb_gsi_request *req, enum gsi_ep_op op)
|
||||
{ return 0; }
|
||||
#endif /* USB_GADGET */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
@ -316,6 +431,7 @@ struct usb_gadget_ops {
|
|||
struct usb_ep *(*match_ep)(struct usb_gadget *,
|
||||
struct usb_endpoint_descriptor *,
|
||||
struct usb_ss_ep_comp_descriptor *);
|
||||
int (*restart)(struct usb_gadget *g);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -421,6 +537,7 @@ struct usb_gadget {
|
|||
unsigned deactivated:1;
|
||||
unsigned connected:1;
|
||||
unsigned lpm_capable:1;
|
||||
unsigned remote_wakeup:1;
|
||||
};
|
||||
#define work_to_gadget(w) (container_of((w), struct usb_gadget, work))
|
||||
|
||||
|
@ -1029,5 +1146,37 @@ extern struct usb_ep *usb_ep_autoconfig_ss(struct usb_gadget *,
|
|||
extern void usb_ep_autoconfig_release(struct usb_ep *);
|
||||
|
||||
extern void usb_ep_autoconfig_reset(struct usb_gadget *);
|
||||
extern struct usb_ep *usb_ep_autoconfig_by_name(struct usb_gadget *gadget,
|
||||
struct usb_endpoint_descriptor *desc,
|
||||
const char *ep_name);
|
||||
|
||||
#ifdef CONFIG_USB_DWC3_MSM
|
||||
int msm_ep_config(struct usb_ep *ep, struct usb_request *request);
|
||||
int msm_ep_unconfig(struct usb_ep *ep);
|
||||
void dwc3_tx_fifo_resize_request(struct usb_ep *ep, bool qdss_enable);
|
||||
int msm_data_fifo_config(struct usb_ep *ep, unsigned long addr, u32 size,
|
||||
u8 dst_pipe_idx);
|
||||
bool msm_dwc3_reset_ep_after_lpm(struct usb_gadget *gadget);
|
||||
int msm_dwc3_reset_dbm_ep(struct usb_ep *ep);
|
||||
#else
|
||||
static inline int msm_data_fifo_config(struct usb_ep *ep, unsigned long addr,
|
||||
u32 size, u8 dst_pipe_idx)
|
||||
{ return -ENODEV; }
|
||||
|
||||
static inline int msm_ep_config(struct usb_ep *ep, struct usb_request *request)
|
||||
{ return -ENODEV; }
|
||||
|
||||
static inline int msm_ep_unconfig(struct usb_ep *ep)
|
||||
{ return -ENODEV; }
|
||||
|
||||
static inline void dwc3_tx_fifo_resize_request(struct usb_ep *ep,
|
||||
bool qdss_enable)
|
||||
{ }
|
||||
|
||||
static inline bool msm_dwc3_reset_ep_after_lpm(struct usb_gadget *gadget)
|
||||
{ return false; }
|
||||
|
||||
static inline int msm_dwc3_reset_dbm_ep(struct usb_ep *ep)
|
||||
{ return -ENODEV; }
|
||||
#endif
|
||||
#endif /* __LINUX_USB_GADGET_H */
|
||||
|
|
|
@ -60,6 +60,7 @@ enum usb_otg_state {
|
|||
OTG_STATE_B_IDLE,
|
||||
OTG_STATE_B_SRP_INIT,
|
||||
OTG_STATE_B_PERIPHERAL,
|
||||
OTG_STATE_B_SUSPEND,
|
||||
|
||||
/* extra dual-role default-b states */
|
||||
OTG_STATE_B_WAIT_ACON,
|
||||
|
|
Loading…
Add table
Reference in a new issue