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:
Jack Pham 2018-01-15 16:37:05 -08:00 committed by Hemant Kumar
parent 5a5e9243e0
commit 6252350019
14 changed files with 1239 additions and 262 deletions
Documentation/devicetree/bindings/usb
drivers/usb
include/linux/usb

View file

@ -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.

View file

@ -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

View file

@ -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);

View file

@ -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 */

View file

@ -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;
}

View file

@ -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, &params);
dep->flags |= DWC3_EP_BUSY;
if (ret < 0)
dev_dbg(dwc->dev, "UpdateXfr fail on GSI EP#%d\n", dep->number);
return ret;

View file

@ -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(&params, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
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

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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 */

View file

@ -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,