Merge tag 'usb-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB/PHY driver updates from Greg KH:
 "Here are the large set of USB and PHY driver updates for 5.8-rc1.

  Nothing huge, just lots of little things:

   - USB gadget fixes and additions all over the place

   - new PHY drivers

   - PHY driver fixes and updates

   - XHCI driver updates

   - musb driver updates

   - more USB-serial driver ids added

   - various USB quirks added

   - thunderbolt minor updates and fixes

   - typec updates and additions

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'usb-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (245 commits)
  usb: dwc3: meson-g12a: fix USB2 PHY initialization on G12A and A1 SoCs
  usb: dwc3: meson-g12a: fix error path when fetching the reset line fails
  Revert "dt-bindings: usb: qcom,dwc3: Convert USB DWC3 bindings"
  Revert "dt-bindings: usb: qcom,dwc3: Add compatible for SC7180"
  Revert "dt-bindings: usb: qcom,dwc3: Introduce interconnect properties for Qualcomm DWC3 driver"
  USB: serial: ch341: fix lockup of devices with limited prescaler
  USB: serial: ch341: add basis for quirk detection
  CDC-ACM: heed quirk also in error handling
  USB: serial: option: add Telit LE910C1-EUX compositions
  usb: musb: Fix runtime PM imbalance on error
  usb: musb: jz4740: Prevent lockup when CONFIG_SMP is set
  usb: musb: mediatek: add reset FADDR to zero in reset interrupt handle
  usb: musb: use true for 'use_dma'
  usb: musb: start session in resume for host port
  usb: musb: return -ESHUTDOWN in urb when three-strikes error happened
  USB: serial: qcserial: add DW5816e QDL support
  thunderbolt: Add trivial .shutdown
  usb: dwc3: keystone: Turn on USB3 PHY before controller
  dt-bindings: usb: ti,keystone-dwc3.yaml: Add USB3.0 PHY property
  dt-bindings: usb: convert keystone-usb.txt to YAML
  ...
This commit is contained in:
Linus Torvalds
2020-06-07 09:42:16 -07:00
298 changed files with 7743 additions and 2673 deletions

View File

@@ -138,7 +138,7 @@ static int cdns_ti_probe(struct platform_device *pdev)
error = pm_runtime_get_sync(dev);
if (error < 0) {
dev_err(dev, "pm_runtime_get_sync failed: %d\n", error);
goto err_get;
goto err;
}
/* assert RESET */
@@ -185,7 +185,6 @@ static int cdns_ti_probe(struct platform_device *pdev)
err:
pm_runtime_put_sync(data->dev);
err_get:
pm_runtime_disable(data->dev);
return error;

View File

@@ -82,8 +82,6 @@ static void cdns3_exit_roles(struct cdns3 *cdns)
cdns3_drd_exit(cdns);
}
static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns);
/**
* cdns3_core_init_role - initialize role of operation
* @cdns: Pointer to cdns3 structure
@@ -193,12 +191,12 @@ err:
}
/**
* cdsn3_hw_role_state_machine - role switch state machine based on hw events.
* cdns3_hw_role_state_machine - role switch state machine based on hw events.
* @cdns: Pointer to controller structure.
*
* Returns next role to be entered based on hw events.
*/
static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns)
static enum usb_role cdns3_hw_role_state_machine(struct cdns3 *cdns)
{
enum usb_role role;
int id, vbus;
@@ -291,14 +289,10 @@ int cdns3_hw_role_switch(struct cdns3 *cdns)
enum usb_role real_role, current_role;
int ret = 0;
/* Do nothing if role based on syfs. */
if (cdns->role_override)
return 0;
pm_runtime_get_sync(cdns->dev);
current_role = cdns->role;
real_role = cdsn3_hw_role_state_machine(cdns);
real_role = cdns3_hw_role_state_machine(cdns);
/* Do nothing if nothing changed */
if (current_role == real_role)
@@ -353,39 +347,6 @@ static int cdns3_role_set(struct usb_role_switch *sw, enum usb_role role)
pm_runtime_get_sync(cdns->dev);
/*
* FIXME: switch role framework should be extended to meet
* requirements. Driver assumes that role can be controlled
* by SW or HW. Temporary workaround is to use USB_ROLE_NONE to
* switch from SW to HW control.
*
* For dr_mode == USB_DR_MODE_OTG:
* if user sets USB_ROLE_HOST or USB_ROLE_DEVICE then driver
* sets role_override flag and forces that role.
* if user sets USB_ROLE_NONE, driver clears role_override and lets
* HW state machine take over.
*
* For dr_mode != USB_DR_MODE_OTG:
* Assumptions:
* 1. Restricted user control between NONE and dr_mode.
* 2. Driver doesn't need to rely on role_override flag.
* 3. Driver needs to ensure that HW state machine is never called
* if dr_mode != USB_DR_MODE_OTG.
*/
if (role == USB_ROLE_NONE)
cdns->role_override = 0;
else
cdns->role_override = 1;
/*
* HW state might have changed so driver need to trigger
* HW state machine if dr_mode == USB_DR_MODE_OTG.
*/
if (!cdns->role_override && cdns->dr_mode == USB_DR_MODE_OTG) {
cdns3_hw_role_switch(cdns);
goto pm_put;
}
if (cdns->role == role)
goto pm_put;
@@ -528,6 +489,8 @@ static int cdns3_probe(struct platform_device *pdev)
sw_desc.get = cdns3_role_get;
sw_desc.allow_userspace_control = true;
sw_desc.driver_data = cdns;
if (device_property_read_bool(dev, "usb-role-switch"))
sw_desc.fwnode = dev->fwnode;
cdns->role_sw = usb_role_switch_register(dev, &sw_desc);
if (IS_ERR(cdns->role_sw)) {

View File

@@ -62,7 +62,6 @@ struct cdns3_role_driver {
* This field based on firmware setting, kernel configuration
* and hardware configuration.
* @role_sw: pointer to role switch object.
* @role_override: set 1 if role rely on SW.
*/
struct cdns3 {
struct device *dev;
@@ -90,7 +89,6 @@ struct cdns3 {
struct mutex mutex;
enum usb_dr_mode dr_mode;
struct usb_role_switch *role_sw;
int role_override;
};
int cdns3_hw_role_switch(struct cdns3 *cdns);

View File

@@ -329,7 +329,7 @@ int cdns3_drd_init(struct cdns3 *cdns)
cdns->otg_v1_regs = NULL;
cdns->otg_regs = regs;
writel(1, &cdns->otg_v0_regs->simulate);
dev_info(cdns->dev, "DRD version v0 (%08x)\n",
dev_dbg(cdns->dev, "DRD version v0 (%08x)\n",
readl(&cdns->otg_v0_regs->version));
} else {
cdns->otg_v0_regs = NULL;
@@ -337,7 +337,7 @@ int cdns3_drd_init(struct cdns3 *cdns)
cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
cdns->version = CDNS3_CONTROLLER_V1;
writel(1, &cdns->otg_v1_regs->simulate);
dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
dev_dbg(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
readl(&cdns->otg_v1_regs->did),
readl(&cdns->otg_v1_regs->rid));
}

View File

@@ -332,13 +332,6 @@ static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
case TEST_K:
case TEST_SE0_NAK:
case TEST_PACKET:
cdns3_ep0_complete_setup(priv_dev, 0, 1);
/**
* Little delay to give the controller some time
* for sending status stage.
* This time should be less then 3ms.
*/
mdelay(1);
cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
USB_CMD_STMODE |
USB_STS_TMODE_SEL(tmode - 1));

View File

@@ -512,8 +512,8 @@ static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
}
static struct usb_request *cdns3_wa2_gadget_giveback(struct cdns3_device *priv_dev,
struct cdns3_endpoint *priv_ep,
struct cdns3_request *priv_req)
struct cdns3_endpoint *priv_ep,
struct cdns3_request *priv_req)
{
if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN &&
priv_req->flags & REQUEST_INTERNAL) {
@@ -552,8 +552,8 @@ static struct usb_request *cdns3_wa2_gadget_giveback(struct cdns3_device *priv_d
}
static int cdns3_wa2_gadget_ep_queue(struct cdns3_device *priv_dev,
struct cdns3_endpoint *priv_ep,
struct cdns3_request *priv_req)
struct cdns3_endpoint *priv_ep,
struct cdns3_request *priv_req)
{
int deferred = 0;
@@ -1905,7 +1905,7 @@ static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
}
static void cdns3_stream_ep_reconfig(struct cdns3_device *priv_dev,
struct cdns3_endpoint *priv_ep)
struct cdns3_endpoint *priv_ep)
{
if (!priv_ep->use_streams || priv_dev->gadget.speed < USB_SPEED_SUPER)
return;
@@ -1926,7 +1926,7 @@ static void cdns3_stream_ep_reconfig(struct cdns3_device *priv_dev,
}
static void cdns3_configure_dmult(struct cdns3_device *priv_dev,
struct cdns3_endpoint *priv_ep)
struct cdns3_endpoint *priv_ep)
{
struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
@@ -2965,7 +2965,7 @@ static int cdns3_init_eps(struct cdns3_device *priv_dev)
priv_ep->flags = 0;
dev_info(priv_dev->dev, "Initialized %s support: %s %s\n",
dev_dbg(priv_dev->dev, "Initialized %s support: %s %s\n",
priv_ep->name,
priv_ep->endpoint.caps.type_bulk ? "BULK, INT" : "",
priv_ep->endpoint.caps.type_iso ? "ISO" : "");
@@ -3069,6 +3069,7 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
priv_dev->gadget.name = "usb-ss-gadget";
priv_dev->gadget.sg_supported = 1;
priv_dev->gadget.quirk_avoids_skb_reserve = 1;
priv_dev->gadget.irq = cdns->dev_irq;
spin_lock_init(&priv_dev->lock);
INIT_WORK(&priv_dev->pending_status_wq,

View File

@@ -18,17 +18,6 @@ config USB_CHIPIDEA
if USB_CHIPIDEA
config USB_CHIPIDEA_OF
tristate
depends on OF
default USB_CHIPIDEA
config USB_CHIPIDEA_PCI
tristate
depends on USB_PCI
depends on NOP_USB_XCEIV
default USB_CHIPIDEA
config USB_CHIPIDEA_UDC
bool "ChipIdea device controller"
depends on USB_GADGET
@@ -43,4 +32,30 @@ config USB_CHIPIDEA_HOST
help
Say Y here to enable host controller functionality of the
ChipIdea driver.
config USB_CHIPIDEA_PCI
tristate "Enable PCI glue driver" if EMBEDDED
depends on USB_PCI
depends on NOP_USB_XCEIV
default USB_CHIPIDEA
config USB_CHIPIDEA_MSM
tristate "Enable MSM hsusb glue driver" if EMBEDDED
default USB_CHIPIDEA
config USB_CHIPIDEA_IMX
tristate "Enable i.MX USB glue driver" if EMBEDDED
depends on OF
default USB_CHIPIDEA
config USB_CHIPIDEA_GENERIC
tristate "Enable generic USB2 glue driver" if EMBEDDED
default USB_CHIPIDEA
config USB_CHIPIDEA_TEGRA
tristate "Enable Tegra UDC glue driver" if EMBEDDED
depends on OF
depends on USB_CHIPIDEA_UDC
default USB_CHIPIDEA
endif

View File

@@ -8,11 +8,8 @@ ci_hdrc-$(CONFIG_USB_OTG_FSM) += otg_fsm.o
# Glue/Bridge layers go here
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_usb2.o
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_msm.o
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc_zevio.o
obj-$(CONFIG_USB_CHIPIDEA_PCI) += ci_hdrc_pci.o
obj-$(CONFIG_USB_CHIPIDEA_OF) += usbmisc_imx.o ci_hdrc_imx.o
obj-$(CONFIG_USB_CHIPIDEA_OF) += ci_hdrc_tegra.o
obj-$(CONFIG_USB_CHIPIDEA_GENERIC) += ci_hdrc_usb2.o
obj-$(CONFIG_USB_CHIPIDEA_MSM) += ci_hdrc_msm.o
obj-$(CONFIG_USB_CHIPIDEA_PCI) += ci_hdrc_pci.o
obj-$(CONFIG_USB_CHIPIDEA_IMX) += ci_hdrc_imx.o usbmisc_imx.o
obj-$(CONFIG_USB_CHIPIDEA_TEGRA) += ci_hdrc_tegra.o

View File

@@ -25,6 +25,7 @@
#define TD_PAGE_COUNT 5
#define CI_HDRC_PAGE_SIZE 4096ul /* page size for TD's */
#define ENDPT_MAX 32
#define CI_MAX_BUF_SIZE (TD_PAGE_COUNT * CI_HDRC_PAGE_SIZE)
/******************************************************************************
* REGISTERS

View File

@@ -271,6 +271,7 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
struct device *dev = ci->dev->parent;
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret = 0;
struct imx_usbmisc_data *mdata = data->usbmisc_data;
switch (event) {
case CI_HDRC_IMX_HSIC_ACTIVE_EVENT:
@@ -284,11 +285,19 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
}
break;
case CI_HDRC_IMX_HSIC_SUSPEND_EVENT:
ret = imx_usbmisc_hsic_set_connect(data->usbmisc_data);
ret = imx_usbmisc_hsic_set_connect(mdata);
if (ret)
dev_err(dev,
"hsic_set_connect failed, err=%d\n", ret);
break;
case CI_HDRC_CONTROLLER_VBUS_EVENT:
if (ci->vbus_active)
ret = imx_usbmisc_charger_detection(mdata, true);
else
ret = imx_usbmisc_charger_detection(mdata, false);
if (ci->usb_phy)
schedule_work(&ci->usb_phy->chg_work);
break;
default:
break;
}
@@ -414,6 +423,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
}
pdata.usb_phy = data->phy;
if (data->usbmisc_data)
data->usbmisc_data->usb_phy = data->phy;
if ((of_device_is_compatible(np, "fsl,imx53-usb") ||
of_device_is_compatible(np, "fsl,imx51-usb")) && pdata.usb_phy &&

View File

@@ -24,6 +24,7 @@ struct imx_usbmisc_data {
unsigned int hsic:1; /* HSIC controlller */
unsigned int ext_id:1; /* ID from exteranl event */
unsigned int ext_vbus:1; /* Vbus from exteranl event */
struct usb_phy *usb_phy;
};
int imx_usbmisc_init(struct imx_usbmisc_data *data);
@@ -31,5 +32,6 @@ int imx_usbmisc_init_post(struct imx_usbmisc_data *data);
int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled);
int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data);
int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on);
int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect);
#endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */

View File

@@ -28,13 +28,19 @@ static const struct ci_hdrc_platform_data ci_default_pdata = {
.flags = CI_HDRC_DISABLE_STREAMING,
};
static struct ci_hdrc_platform_data ci_zynq_pdata = {
static const struct ci_hdrc_platform_data ci_zynq_pdata = {
.capoffset = DEF_CAPOFFSET,
};
static const struct ci_hdrc_platform_data ci_zevio_pdata = {
.capoffset = DEF_CAPOFFSET,
.flags = CI_HDRC_REGS_SHARED | CI_HDRC_FORCE_FULLSPEED,
};
static const struct of_device_id ci_hdrc_usb2_of_match[] = {
{ .compatible = "chipidea,usb2"},
{ .compatible = "xlnx,zynq-usb-2.20a", .data = &ci_zynq_pdata},
{ .compatible = "chipidea,usb2" },
{ .compatible = "xlnx,zynq-usb-2.20a", .data = &ci_zynq_pdata },
{ .compatible = "lsi,zevio-usb", .data = &ci_zevio_pdata },
{ }
};
MODULE_DEVICE_TABLE(of, ci_hdrc_usb2_of_match);
@@ -64,13 +70,14 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev)
if (!priv)
return -ENOMEM;
priv->clk = devm_clk_get(dev, NULL);
if (!IS_ERR(priv->clk)) {
ret = clk_prepare_enable(priv->clk);
if (ret) {
dev_err(dev, "failed to enable the clock: %d\n", ret);
return ret;
}
priv->clk = devm_clk_get_optional(dev, NULL);
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
ret = clk_prepare_enable(priv->clk);
if (ret) {
dev_err(dev, "failed to enable the clock: %d\n", ret);
return ret;
}
ci_pdata->name = dev_name(dev);
@@ -94,8 +101,7 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev)
return 0;
clk_err:
if (!IS_ERR(priv->clk))
clk_disable_unprepare(priv->clk);
clk_disable_unprepare(priv->clk);
return ret;
}

View File

@@ -1,67 +0,0 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au>
*
* Based off drivers/usb/chipidea/ci_hdrc_msm.c
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/usb/gadget.h>
#include <linux/usb/chipidea.h>
#include "ci.h"
static struct ci_hdrc_platform_data ci_hdrc_zevio_platdata = {
.name = "ci_hdrc_zevio",
.flags = CI_HDRC_REGS_SHARED | CI_HDRC_FORCE_FULLSPEED,
.capoffset = DEF_CAPOFFSET,
};
static int ci_hdrc_zevio_probe(struct platform_device *pdev)
{
struct platform_device *ci_pdev;
dev_dbg(&pdev->dev, "ci_hdrc_zevio_probe\n");
ci_pdev = ci_hdrc_add_device(&pdev->dev,
pdev->resource, pdev->num_resources,
&ci_hdrc_zevio_platdata);
if (IS_ERR(ci_pdev)) {
dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
return PTR_ERR(ci_pdev);
}
platform_set_drvdata(pdev, ci_pdev);
return 0;
}
static int ci_hdrc_zevio_remove(struct platform_device *pdev)
{
struct platform_device *ci_pdev = platform_get_drvdata(pdev);
ci_hdrc_remove_device(ci_pdev);
return 0;
}
static const struct of_device_id ci_hdrc_zevio_dt_ids[] = {
{ .compatible = "lsi,zevio-usb", },
{ /* sentinel */ }
};
static struct platform_driver ci_hdrc_zevio_driver = {
.probe = ci_hdrc_zevio_probe,
.remove = ci_hdrc_zevio_remove,
.driver = {
.name = "zevio_usb",
.of_match_table = ci_hdrc_zevio_dt_ids,
},
};
MODULE_DEVICE_TABLE(of, ci_hdrc_zevio_dt_ids);
module_platform_driver(ci_hdrc_zevio_driver);
MODULE_LICENSE("GPL v2");

View File

@@ -3,42 +3,16 @@
* core.c - ChipIdea USB IP core family device controller
*
* Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
* Copyright (C) 2020 NXP
*
* Author: David Lopo
*/
/*
* Description: ChipIdea USB IP core family device controller
* Peter Chen <peter.chen@nxp.com>
*
* This driver is composed of several blocks:
* - HW: hardware interface
* - DBG: debug facilities (optional)
* - UTIL: utilities
* - ISR: interrupts handling
* - ENDPT: endpoint operations (Gadget API)
* - GADGET: gadget operations (Gadget API)
* - BUS: bus glue code, bus abstraction layer
*
* Compile Options
* - STALL_IN: non-empty bulk-in pipes cannot be halted
* if defined mass storage compliance succeeds but with warnings
* => case 4: Hi > Dn
* => case 5: Hi > Di
* => case 8: Hi <> Do
* if undefined usbtest 13 fails
* - TRACE: enable function tracing (depends on DEBUG)
*
* Main Features
* - Chapter 9 & Mass Storage Compliance with Gadget File Storage
* - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined)
* - Normal & LPM support
*
* USBTEST Report
* - OK: 0-12, 13 (STALL_IN defined) & 14
* - Not Supported: 15 & 16 (ISO)
*
* TODO List
* - Suspend & Remote Wakeup
* Main Features:
* - Four transfers are supported, usbtest is passed
* - USB Certification for gadget: CH9 and Mass Storage are passed
* - Low power mode
* - USB wakeup
*/
#include <linux/delay.h>
#include <linux/device.h>
@@ -272,7 +246,7 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
ci->rev = ci_get_revision(ci);
dev_dbg(ci->dev,
"ChipIdea HDRC found, revision: %d, lpm: %d; cap: %p op: %p\n",
"revision: %d, lpm: %d; cap: %px op: %px\n",
ci->rev, ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op);
/* setup lock mode ? */
@@ -666,6 +640,7 @@ static int ci_usb_role_switch_set(struct usb_role_switch *sw,
static struct usb_role_switch_desc ci_role_switch = {
.set = ci_usb_role_switch_set,
.get = ci_usb_role_switch_get,
.allow_userspace_control = true,
};
static int ci_get_platdata(struct device *dev,
@@ -1149,8 +1124,11 @@ static int ci_hdrc_probe(struct platform_device *pdev)
if (!ci_otg_is_fsm_mode(ci)) {
/* only update vbus status for peripheral */
if (ci->role == CI_ROLE_GADGET)
if (ci->role == CI_ROLE_GADGET) {
/* Pull down DP for possible charger detection */
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
ci_handle_vbus_change(ci);
}
ret = ci_role_start(ci, ci->role);
if (ret) {

View File

@@ -338,7 +338,7 @@ static int hw_usb_reset(struct ci_hdrc *ci)
*****************************************************************************/
static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
unsigned length)
unsigned int length, struct scatterlist *s)
{
int i;
u32 temp;
@@ -366,7 +366,13 @@ static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
node->ptr->token |= cpu_to_le32(mul << __ffs(TD_MULTO));
}
temp = (u32) (hwreq->req.dma + hwreq->req.actual);
if (s) {
temp = (u32) (sg_dma_address(s) + hwreq->req.actual);
node->td_remaining_size = CI_MAX_BUF_SIZE - length;
} else {
temp = (u32) (hwreq->req.dma + hwreq->req.actual);
}
if (length) {
node->ptr->page[0] = cpu_to_le32(temp);
for (i = 1; i < TD_PAGE_COUNT; i++) {
@@ -400,6 +406,122 @@ static inline u8 _usb_addr(struct ci_hw_ep *ep)
return ((ep->dir == TX) ? USB_ENDPOINT_DIR_MASK : 0) | ep->num;
}
static int prepare_td_for_non_sg(struct ci_hw_ep *hwep,
struct ci_hw_req *hwreq)
{
unsigned int rest = hwreq->req.length;
int pages = TD_PAGE_COUNT;
int ret = 0;
if (rest == 0) {
ret = add_td_to_list(hwep, hwreq, 0, NULL);
if (ret < 0)
return ret;
}
/*
* The first buffer could be not page aligned.
* In that case we have to span into one extra td.
*/
if (hwreq->req.dma % PAGE_SIZE)
pages--;
while (rest > 0) {
unsigned int count = min(hwreq->req.length - hwreq->req.actual,
(unsigned int)(pages * CI_HDRC_PAGE_SIZE));
ret = add_td_to_list(hwep, hwreq, count, NULL);
if (ret < 0)
return ret;
rest -= count;
}
if (hwreq->req.zero && hwreq->req.length && hwep->dir == TX
&& (hwreq->req.length % hwep->ep.maxpacket == 0)) {
ret = add_td_to_list(hwep, hwreq, 0, NULL);
if (ret < 0)
return ret;
}
return ret;
}
static int prepare_td_per_sg(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
struct scatterlist *s)
{
unsigned int rest = sg_dma_len(s);
int ret = 0;
hwreq->req.actual = 0;
while (rest > 0) {
unsigned int count = min_t(unsigned int, rest,
CI_MAX_BUF_SIZE);
ret = add_td_to_list(hwep, hwreq, count, s);
if (ret < 0)
return ret;
rest -= count;
}
return ret;
}
static void ci_add_buffer_entry(struct td_node *node, struct scatterlist *s)
{
int empty_td_slot_index = (CI_MAX_BUF_SIZE - node->td_remaining_size)
/ CI_HDRC_PAGE_SIZE;
int i;
node->ptr->token +=
cpu_to_le32(sg_dma_len(s) << __ffs(TD_TOTAL_BYTES));
for (i = empty_td_slot_index; i < TD_PAGE_COUNT; i++) {
u32 page = (u32) sg_dma_address(s) +
(i - empty_td_slot_index) * CI_HDRC_PAGE_SIZE;
page &= ~TD_RESERVED_MASK;
node->ptr->page[i] = cpu_to_le32(page);
}
}
static int prepare_td_for_sg(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
{
struct usb_request *req = &hwreq->req;
struct scatterlist *s = req->sg;
int ret = 0, i = 0;
struct td_node *node = NULL;
if (!s || req->zero || req->length == 0) {
dev_err(hwep->ci->dev, "not supported operation for sg\n");
return -EINVAL;
}
while (i++ < req->num_mapped_sgs) {
if (sg_dma_address(s) % PAGE_SIZE) {
dev_err(hwep->ci->dev, "not page aligned sg buffer\n");
return -EINVAL;
}
if (node && (node->td_remaining_size >= sg_dma_len(s))) {
ci_add_buffer_entry(node, s);
node->td_remaining_size -= sg_dma_len(s);
} else {
ret = prepare_td_per_sg(hwep, hwreq, s);
if (ret)
return ret;
node = list_entry(hwreq->tds.prev,
struct td_node, td);
}
s = sg_next(s);
}
return ret;
}
/**
* _hardware_enqueue: configures a request at hardware level
* @hwep: endpoint
@@ -411,8 +533,6 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
{
struct ci_hdrc *ci = hwep->ci;
int ret = 0;
unsigned rest = hwreq->req.length;
int pages = TD_PAGE_COUNT;
struct td_node *firstnode, *lastnode;
/* don't queue twice */
@@ -426,35 +546,13 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
if (ret)
return ret;
/*
* The first buffer could be not page aligned.
* In that case we have to span into one extra td.
*/
if (hwreq->req.dma % PAGE_SIZE)
pages--;
if (hwreq->req.num_mapped_sgs)
ret = prepare_td_for_sg(hwep, hwreq);
else
ret = prepare_td_for_non_sg(hwep, hwreq);
if (rest == 0) {
ret = add_td_to_list(hwep, hwreq, 0);
if (ret < 0)
goto done;
}
while (rest > 0) {
unsigned count = min(hwreq->req.length - hwreq->req.actual,
(unsigned)(pages * CI_HDRC_PAGE_SIZE));
ret = add_td_to_list(hwep, hwreq, count);
if (ret < 0)
goto done;
rest -= count;
}
if (hwreq->req.zero && hwreq->req.length && hwep->dir == TX
&& (hwreq->req.length % hwep->ep.maxpacket == 0)) {
ret = add_td_to_list(hwep, hwreq, 0);
if (ret < 0)
goto done;
}
if (ret)
return ret;
firstnode = list_first_entry(&hwreq->tds, struct td_node, td);
@@ -1561,6 +1659,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
{
struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
unsigned long flags;
int ret = 0;
spin_lock_irqsave(&ci->lock, flags);
ci->vbus_active = is_active;
@@ -1570,10 +1669,14 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
usb_phy_set_charger_state(ci->usb_phy, is_active ?
USB_CHARGER_PRESENT : USB_CHARGER_ABSENT);
if (ci->platdata->notify_event)
ret = ci->platdata->notify_event(ci,
CI_HDRC_CONTROLLER_VBUS_EVENT);
if (ci->driver)
ci_hdrc_gadget_connect(_gadget, is_active);
return 0;
return ret;
}
static int ci_udc_wakeup(struct usb_gadget *_gadget)
@@ -1936,6 +2039,7 @@ static int udc_start(struct ci_hdrc *ci)
ci->gadget.max_speed = USB_SPEED_HIGH;
ci->gadget.name = ci->platdata->name;
ci->gadget.otg_caps = otg_caps;
ci->gadget.sg_supported = 1;
if (ci->platdata->flags & CI_HDRC_REQUIRES_ALIGNED_DMA)
ci->gadget.quirk_avoids_skb_reserve = 1;

View File

@@ -61,16 +61,14 @@ struct td_node {
struct list_head td;
dma_addr_t dma;
struct ci_hw_td *ptr;
int td_remaining_size;
};
/**
* struct ci_hw_req - usb request representation
* @req: request structure for gadget drivers
* @queue: link to QH list
* @ptr: transfer descriptor for this request
* @dma: dma address for the transfer descriptor
* @zptr: transfer descriptor for the zero packet
* @zdma: dma address of the zero packet's transfer descriptor
* @tds: link to TD list
*/
struct ci_hw_req {
struct usb_request req;

View File

@@ -8,6 +8,7 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/usb/otg.h>
#include "ci_hdrc_imx.h"
@@ -99,6 +100,33 @@
#define MX7D_USB_VBUS_WAKEUP_SOURCE_AVALID MX7D_USB_VBUS_WAKEUP_SOURCE(1)
#define MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID MX7D_USB_VBUS_WAKEUP_SOURCE(2)
#define MX7D_USB_VBUS_WAKEUP_SOURCE_SESS_END MX7D_USB_VBUS_WAKEUP_SOURCE(3)
#define MX7D_USBNC_AUTO_RESUME BIT(2)
/* The default DM/DP value is pull-down */
#define MX7D_USBNC_USB_CTRL2_OPMODE(v) (v << 6)
#define MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING MX7D_USBNC_USB_CTRL2_OPMODE(1)
#define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK (BIT(7) | BIT(6))
#define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN BIT(8)
#define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_VAL BIT(12)
#define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_EN BIT(13)
#define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_VAL BIT(14)
#define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_EN BIT(15)
#define MX7D_USBNC_USB_CTRL2_DP_DM_MASK (BIT(12) | BIT(13) | \
BIT(14) | BIT(15))
#define MX7D_USB_OTG_PHY_CFG1 0x30
#define MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL BIT(0)
#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 BIT(1)
#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 BIT(2)
#define MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB BIT(3)
#define MX7D_USB_OTG_PHY_CFG2_DRVVBUS0 BIT(16)
#define MX7D_USB_OTG_PHY_CFG2 0x34
#define MX7D_USB_OTG_PHY_STATUS 0x3c
#define MX7D_USB_OTG_PHY_STATUS_LINE_STATE0 BIT(0)
#define MX7D_USB_OTG_PHY_STATUS_LINE_STATE1 BIT(1)
#define MX7D_USB_OTG_PHY_STATUS_VBUS_VLD BIT(3)
#define MX7D_USB_OTG_PHY_STATUS_CHRGDET BIT(29)
#define MX6_USB_OTG_WAKEUP_BITS (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | \
MX6_BM_ID_WAKEUP)
@@ -114,6 +142,8 @@ struct usbmisc_ops {
int (*hsic_set_connect)(struct imx_usbmisc_data *data);
/* It's called during suspend/resume */
int (*hsic_set_clk)(struct imx_usbmisc_data *data, bool enabled);
/* usb charger detection */
int (*charger_detection)(struct imx_usbmisc_data *data);
};
struct imx_usbmisc {
@@ -609,10 +639,263 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
reg |= MX6_BM_PWR_POLARITY;
writel(reg, usbmisc->base);
reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID,
usbmisc->base + MX7D_USBNC_USB_CTRL2);
/* SoC non-burst setting */
reg = readl(usbmisc->base);
writel(reg | MX6_BM_NON_BURST_SETTING, usbmisc->base);
if (!data->hsic) {
reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID
| MX7D_USBNC_AUTO_RESUME,
usbmisc->base + MX7D_USBNC_USB_CTRL2);
}
spin_unlock_irqrestore(&usbmisc->lock, flags);
usbmisc_imx7d_set_wakeup(data, false);
return 0;
}
static int imx7d_charger_secondary_detection(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
struct usb_phy *usb_phy = data->usb_phy;
int val;
unsigned long flags;
/* VDM_SRC is connected to D- and IDP_SINK is connected to D+ */
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 |
MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL,
usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
spin_unlock_irqrestore(&usbmisc->lock, flags);
usleep_range(1000, 2000);
/*
* Per BC 1.2, check voltage of D+:
* DCP: if greater than VDAT_REF;
* CDP: if less than VDAT_REF.
*/
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
if (val & MX7D_USB_OTG_PHY_STATUS_CHRGDET) {
dev_dbg(data->dev, "It is a dedicate charging port\n");
usb_phy->chg_type = DCP_TYPE;
} else {
dev_dbg(data->dev, "It is a charging downstream port\n");
usb_phy->chg_type = CDP_TYPE;
}
return 0;
}
static void imx7_disable_charger_detector(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
unsigned long flags;
u32 val;
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
val &= ~(MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB |
MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 |
MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL);
writel(val, usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
/* Set OPMODE to be 2'b00 and disable its override */
val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK;
writel(val, usbmisc->base + MX7D_USBNC_USB_CTRL2);
val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
writel(val & ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN,
usbmisc->base + MX7D_USBNC_USB_CTRL2);
spin_unlock_irqrestore(&usbmisc->lock, flags);
}
static int imx7d_charger_data_contact_detect(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
unsigned long flags;
u32 val;
int i, data_pin_contact_count = 0;
/* Enable Data Contact Detect (DCD) per the USB BC 1.2 */
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB,
usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
spin_unlock_irqrestore(&usbmisc->lock, flags);
for (i = 0; i < 100; i = i + 1) {
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
if (!(val & MX7D_USB_OTG_PHY_STATUS_LINE_STATE0)) {
if (data_pin_contact_count++ > 5)
/* Data pin makes contact */
break;
usleep_range(5000, 10000);
} else {
data_pin_contact_count = 0;
usleep_range(5000, 6000);
}
}
/* Disable DCD after finished data contact check */
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
writel(val & ~MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB,
usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
spin_unlock_irqrestore(&usbmisc->lock, flags);
if (i == 100) {
dev_err(data->dev,
"VBUS is coming from a dedicated power supply.\n");
return -ENXIO;
}
return 0;
}
static int imx7d_charger_primary_detection(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
struct usb_phy *usb_phy = data->usb_phy;
unsigned long flags;
u32 val;
/* VDP_SRC is connected to D+ and IDM_SINK is connected to D- */
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
val &= ~MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL;
writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0,
usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
spin_unlock_irqrestore(&usbmisc->lock, flags);
usleep_range(1000, 2000);
/* Check if D- is less than VDAT_REF to determine an SDP per BC 1.2 */
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
if (!(val & MX7D_USB_OTG_PHY_STATUS_CHRGDET)) {
dev_dbg(data->dev, "It is a standard downstream port\n");
usb_phy->chg_type = SDP_TYPE;
}
return 0;
}
/**
* Whole charger detection process:
* 1. OPMODE override to be non-driving
* 2. Data contact check
* 3. Primary detection
* 4. Secondary detection
* 5. Disable charger detection
*/
static int imx7d_charger_detection(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
struct usb_phy *usb_phy = data->usb_phy;
unsigned long flags;
u32 val;
int ret;
/* Check if vbus is valid */
val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
if (!(val & MX7D_USB_OTG_PHY_STATUS_VBUS_VLD)) {
dev_err(data->dev, "vbus is error\n");
return -EINVAL;
}
/*
* Keep OPMODE to be non-driving mode during the whole
* charger detection process.
*/
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK;
val |= MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING;
writel(val, usbmisc->base + MX7D_USBNC_USB_CTRL2);
val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
writel(val | MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN,
usbmisc->base + MX7D_USBNC_USB_CTRL2);
spin_unlock_irqrestore(&usbmisc->lock, flags);
ret = imx7d_charger_data_contact_detect(data);
if (ret)
return ret;
ret = imx7d_charger_primary_detection(data);
if (!ret && usb_phy->chg_type != SDP_TYPE)
ret = imx7d_charger_secondary_detection(data);
imx7_disable_charger_detector(data);
return ret;
}
static int usbmisc_imx7ulp_init(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
unsigned long flags;
u32 reg;
if (data->index >= 1)
return -EINVAL;
spin_lock_irqsave(&usbmisc->lock, flags);
reg = readl(usbmisc->base);
if (data->disable_oc) {
reg |= MX6_BM_OVER_CUR_DIS;
} else {
reg &= ~MX6_BM_OVER_CUR_DIS;
/*
* If the polarity is not configured keep it as setup by the
* bootloader.
*/
if (data->oc_pol_configured && data->oc_pol_active_low)
reg |= MX6_BM_OVER_CUR_POLARITY;
else if (data->oc_pol_configured)
reg &= ~MX6_BM_OVER_CUR_POLARITY;
}
/* If the polarity is not set keep it as setup by the bootlader */
if (data->pwr_pol == 1)
reg |= MX6_BM_PWR_POLARITY;
writel(reg, usbmisc->base);
/* SoC non-burst setting */
reg = readl(usbmisc->base);
writel(reg | MX6_BM_NON_BURST_SETTING, usbmisc->base);
if (data->hsic) {
reg = readl(usbmisc->base);
writel(reg | MX6_BM_UTMI_ON_CLOCK, usbmisc->base);
reg = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET);
reg |= MX6_BM_HSIC_EN | MX6_BM_HSIC_CLK_ON;
writel(reg, usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET);
/*
* For non-HSIC controller, the autoresume is enabled
* at MXS PHY driver (usbphy_ctrl bit18).
*/
reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
writel(reg | MX7D_USBNC_AUTO_RESUME,
usbmisc->base + MX7D_USBNC_USB_CTRL2);
} else {
reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID,
usbmisc->base + MX7D_USBNC_USB_CTRL2);
}
spin_unlock_irqrestore(&usbmisc->lock, flags);
@@ -659,6 +942,14 @@ static const struct usbmisc_ops imx6sx_usbmisc_ops = {
static const struct usbmisc_ops imx7d_usbmisc_ops = {
.init = usbmisc_imx7d_init,
.set_wakeup = usbmisc_imx7d_set_wakeup,
.charger_detection = imx7d_charger_detection,
};
static const struct usbmisc_ops imx7ulp_usbmisc_ops = {
.init = usbmisc_imx7ulp_init,
.set_wakeup = usbmisc_imx7d_set_wakeup,
.hsic_set_connect = usbmisc_imx6_hsic_set_connect,
.hsic_set_clk = usbmisc_imx6_hsic_set_clk,
};
static inline bool is_imx53_usbmisc(struct imx_usbmisc_data *data)
@@ -737,6 +1028,39 @@ int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on)
return usbmisc->ops->hsic_set_clk(data, on);
}
EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_clk);
int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect)
{
struct imx_usbmisc *usbmisc;
struct usb_phy *usb_phy;
int ret = 0;
if (!data)
return -EINVAL;
usbmisc = dev_get_drvdata(data->dev);
usb_phy = data->usb_phy;
if (!usbmisc->ops->charger_detection)
return -ENOTSUPP;
if (connect) {
ret = usbmisc->ops->charger_detection(data);
if (ret) {
dev_err(data->dev,
"Error occurs during detection: %d\n",
ret);
usb_phy->chg_state = USB_CHARGER_ABSENT;
} else {
usb_phy->chg_state = USB_CHARGER_PRESENT;
}
} else {
usb_phy->chg_state = USB_CHARGER_ABSENT;
usb_phy->chg_type = UNKNOWN_TYPE;
}
return ret;
}
EXPORT_SYMBOL_GPL(imx_usbmisc_charger_detection);
static const struct of_device_id usbmisc_imx_dt_ids[] = {
{
.compatible = "fsl,imx25-usbmisc",
@@ -780,7 +1104,7 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = {
},
{
.compatible = "fsl,imx7ulp-usbmisc",
.data = &imx7d_usbmisc_ops,
.data = &imx7ulp_usbmisc_ops,
},
{ /* sentinel */ }
};

View File

@@ -584,7 +584,7 @@ static void acm_softint(struct work_struct *work)
}
if (test_and_clear_bit(ACM_ERROR_DELAY, &acm->flags)) {
for (i = 0; i < ACM_NR; i++)
for (i = 0; i < acm->rx_buflimit; i++)
if (test_and_clear_bit(i, &acm->urbs_in_error_delay))
acm_submit_read_urb(acm, i, GFP_NOIO);
}

View File

@@ -468,7 +468,8 @@ static int usblp_release(struct inode *inode, struct file *file)
usb_autopm_put_interface(usblp->intf);
if (!usblp->present) /* finish cleanup from disconnect */
usblp_cleanup(usblp);
usblp_cleanup(usblp); /* any URBs must be dead */
mutex_unlock(&usblp_mutex);
return 0;
}
@@ -1375,9 +1376,11 @@ static void usblp_disconnect(struct usb_interface *intf)
usblp_unlink_urbs(usblp);
mutex_unlock(&usblp->mut);
usb_poison_anchored_urbs(&usblp->urbs);
if (!usblp->used)
usblp_cleanup(usblp);
mutex_unlock(&usblp_mutex);
}

View File

@@ -159,6 +159,7 @@ static void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd,
* usb_hcd_pci_probe - initialize PCI-based HCDs
* @dev: USB Host Controller being probed
* @id: pci hotplug id connecting controller to HCD framework
* @driver: USB HC driver handle
* Context: !in_interrupt()
*
* Allocates basic PCI resources for this USB host controller, and
@@ -169,9 +170,9 @@ static void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd,
*
* Return: 0 if successful.
*/
int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id,
const struct hc_driver *driver)
{
struct hc_driver *driver;
struct usb_hcd *hcd;
int retval;
int hcd_irq = 0;
@@ -181,7 +182,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (!id)
return -EINVAL;
driver = (struct hc_driver *)id->driver_data;
if (!driver)
return -EINVAL;

View File

@@ -93,7 +93,7 @@ module_param(old_scheme_first, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(old_scheme_first,
"start with the old device initialization scheme");
static bool use_both_schemes = 1;
static bool use_both_schemes = true;
module_param(use_both_schemes, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(use_both_schemes,
"try the other device initialization scheme if the "

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* usb hub driver head file
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* drivers/usb/core/otg_whitelist.h
*

View File

@@ -1262,8 +1262,10 @@ void usb_create_sysfs_intf_files(struct usb_interface *intf)
if (!alt->string && !(udev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS))
alt->string = usb_cache_string(udev, alt->desc.iInterface);
if (alt->string && device_create_file(&intf->dev, &dev_attr_interface))
; /* We don't actually care if the function fails. */
if (alt->string && device_create_file(&intf->dev, &dev_attr_interface)) {
/* This is not a serious error */
dev_dbg(&intf->dev, "interface string descriptor file not created\n");
}
intf->sysfs_files_created = 1;
}

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Released under the GPLv2 only.
*/

View File

@@ -524,10 +524,25 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
greset |= GRSTCTL_CSFTRST;
dwc2_writel(hsotg, greset, GRSTCTL);
if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_CSFTRST, 10000)) {
dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL GRSTCTL_CSFTRST\n",
__func__);
return -EBUSY;
if ((hsotg->hw_params.snpsid & DWC2_CORE_REV_MASK) <
(DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) {
if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL,
GRSTCTL_CSFTRST, 10000)) {
dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL_CSFTRST\n",
__func__);
return -EBUSY;
}
} else {
if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL,
GRSTCTL_CSFTRST_DONE, 10000)) {
dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL_CSFTRST_DONE\n",
__func__);
return -EBUSY;
}
greset = dwc2_readl(hsotg, GRSTCTL);
greset &= ~GRSTCTL_CSFTRST;
greset |= GRSTCTL_CSFTRST_DONE;
dwc2_writel(hsotg, greset, GRSTCTL);
}
/* Wait for AHB master IDLE state */

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/*
* core.h - DesignWare HS OTG Controller common declarations
*
@@ -1103,8 +1103,10 @@ struct dwc2_hsotg {
#define DWC2_CORE_REV_3_00a 0x4f54300a
#define DWC2_CORE_REV_3_10a 0x4f54310a
#define DWC2_CORE_REV_4_00a 0x4f54400a
#define DWC2_CORE_REV_4_20a 0x4f54420a
#define DWC2_FS_IOT_REV_1_00a 0x5531100a
#define DWC2_HS_IOT_REV_1_00a 0x5532100a
#define DWC2_CORE_REV_MASK 0x0000ffff
/* DWC OTG HW Core ID */
#define DWC2_OTG_ID 0x4f540000
@@ -1309,6 +1311,8 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg);
bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg);
int dwc2_check_core_version(struct dwc2_hsotg *hsotg);
/*
* Common core Functions.
* The following functions support managing the DWC_otg controller in either

View File

@@ -416,10 +416,13 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
if (ret && (ret != -ENOTSUPP))
dev_err(hsotg->dev, "exit power_down failed\n");
/* Change to L0 state */
hsotg->lx_state = DWC2_L0;
call_gadget(hsotg, resume);
} else {
/* Change to L0 state */
hsotg->lx_state = DWC2_L0;
}
/* Change to L0 state */
hsotg->lx_state = DWC2_L0;
} else {
if (hsotg->params.power_down)
return;

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* debug.h - Designware USB2 DRD controller debug header
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/*
* hcd.h - DesignWare HS OTG Controller host-mode declarations
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/*
* hw.h - DesignWare HS OTG Controller hardware definitions
*
@@ -126,6 +126,7 @@
#define GRSTCTL HSOTG_REG(0x010)
#define GRSTCTL_AHBIDLE BIT(31)
#define GRSTCTL_DMAREQ BIT(30)
#define GRSTCTL_CSFTRST_DONE BIT(29)
#define GRSTCTL_TXFNUM_MASK (0x1f << 6)
#define GRSTCTL_TXFNUM_SHIFT 6
#define GRSTCTL_TXFNUM_LIMIT 0x1f

View File

@@ -782,25 +782,6 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4;
u32 grxfsiz;
/*
* Attempt to ensure this device is really a DWC_otg Controller.
* Read and verify the GSNPSID register contents. The value should be
* 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx
*/
hw->snpsid = dwc2_readl(hsotg, GSNPSID);
if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID &&
(hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID &&
(hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) {
dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
hw->snpsid);
return -ENODEV;
}
dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n",
hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
hwcfg1 = dwc2_readl(hsotg, GHWCFG1);
hwcfg2 = dwc2_readl(hsotg, GHWCFG2);
hwcfg3 = dwc2_readl(hsotg, GHWCFG3);

View File

@@ -362,6 +362,37 @@ static bool dwc2_check_core_endianness(struct dwc2_hsotg *hsotg)
return true;
}
/**
* Check core version
*
* @hsotg: Programming view of the DWC_otg controller
*
*/
int dwc2_check_core_version(struct dwc2_hsotg *hsotg)
{
struct dwc2_hw_params *hw = &hsotg->hw_params;
/*
* Attempt to ensure this device is really a DWC_otg Controller.
* Read and verify the GSNPSID register contents. The value should be
* 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx
*/
hw->snpsid = dwc2_readl(hsotg, GSNPSID);
if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID &&
(hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID &&
(hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) {
dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
hw->snpsid);
return -ENODEV;
}
dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n",
hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
return 0;
}
/**
* dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg
* driver
@@ -444,6 +475,14 @@ static int dwc2_driver_probe(struct platform_device *dev)
of_property_read_bool(dev->dev.of_node,
"snps,need-phy-for-wake");
/*
* Before performing any core related operations
* check core version.
*/
retval = dwc2_check_core_version(hsotg);
if (retval)
goto error;
/*
* Reset before dwc2_get_hwparams() then it could get power-on real
* reset value form registers.

View File

@@ -85,7 +85,9 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
* specified or set to OTG, then set the mode to peripheral.
*/
if (mode == USB_DR_MODE_OTG &&
dwc->revision >= DWC3_REVISION_330A)
(!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) ||
!device_property_read_bool(dwc->dev, "usb-role-switch")) &&
!DWC3_VER_IS_PRIOR(DWC3, 330A))
mode = USB_DR_MODE_PERIPHERAL;
}
@@ -121,17 +123,19 @@ static void __dwc3_set_mode(struct work_struct *work)
if (dwc->dr_mode != USB_DR_MODE_OTG)
return;
pm_runtime_get_sync(dwc->dev);
if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
dwc3_otg_update(dwc, 0);
if (!dwc->desired_dr_role)
return;
goto out;
if (dwc->desired_dr_role == dwc->current_dr_role)
return;
goto out;
if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
return;
goto out;
switch (dwc->current_dr_role) {
case DWC3_GCTL_PRTCAP_HOST:
@@ -190,6 +194,9 @@ static void __dwc3_set_mode(struct work_struct *work)
break;
}
out:
pm_runtime_mark_last_busy(dwc->dev);
pm_runtime_put_autosuspend(dwc->dev);
}
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
@@ -257,7 +264,7 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
* take a little more than 50ms. Set the polling rate at 20ms
* for 10 times instead.
*/
if (dwc3_is_usb31(dwc) && dwc->revision >= DWC3_USB31_REVISION_190A)
if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32))
retries = 10;
do {
@@ -265,8 +272,7 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
if (!(reg & DWC3_DCTL_CSFTRST))
goto done;
if (dwc3_is_usb31(dwc) &&
dwc->revision >= DWC3_USB31_REVISION_190A)
if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32))
msleep(20);
else
udelay(1);
@@ -283,7 +289,7 @@ done:
* is cleared, we must wait at least 50ms before accessing the PHY
* domain (synchronization delay).
*/
if (dwc3_is_usb31(dwc) && dwc->revision <= DWC3_USB31_REVISION_180A)
if (DWC3_VER_IS_WITHIN(DWC31, ANY, 180A))
msleep(50);
return 0;
@@ -298,7 +304,7 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
u32 reg;
u32 dft;
if (dwc->revision < DWC3_REVISION_250A)
if (DWC3_VER_IS_PRIOR(DWC3, 250A))
return;
if (dwc->fladj == 0)
@@ -579,7 +585,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
* will be '0' when the core is reset. Application needs to set it
* to '1' after the core initialization is completed.
*/
if (dwc->revision > DWC3_REVISION_194A)
if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A))
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
/*
@@ -670,7 +676,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
* be '0' when the core is reset. Application needs to set it to
* '1' after the core initialization is completed.
*/
if (dwc->revision > DWC3_REVISION_194A)
if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A))
reg |= DWC3_GUSB2PHYCFG_SUSPHY;
/*
@@ -719,15 +725,13 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
u32 reg;
reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
dwc->ip = DWC3_GSNPS_ID(reg);
/* This should read as U3 followed by revision number */
if ((reg & DWC3_GSNPSID_MASK) == 0x55330000) {
/* Detected DWC_usb3 IP */
if (DWC3_IP_IS(DWC3)) {
dwc->revision = reg;
} else if ((reg & DWC3_GSNPSID_MASK) == 0x33310000) {
/* Detected DWC_usb31 IP */
} else if (DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) {
dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER);
dwc->revision |= DWC3_REVISION_IS_DWC31;
dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE);
} else {
return false;
@@ -760,8 +764,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
*/
if ((dwc->dr_mode == USB_DR_MODE_HOST ||
dwc->dr_mode == USB_DR_MODE_OTG) &&
(dwc->revision >= DWC3_REVISION_210A &&
dwc->revision <= DWC3_REVISION_250A))
DWC3_VER_IS_WITHIN(DWC3, 210A, 250A))
reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC;
else
reg &= ~DWC3_GCTL_DSBLCLKGTNG;
@@ -804,7 +807,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
* and falls back to high-speed mode which causes
* the device to enter a Connect/Disconnect loop
*/
if (dwc->revision < DWC3_REVISION_190A)
if (DWC3_VER_IS_PRIOR(DWC3, 190A))
reg |= DWC3_GCTL_U2RSTECN;
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
@@ -957,7 +960,7 @@ static int dwc3_core_init(struct dwc3 *dwc)
goto err0a;
if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD &&
dwc->revision > DWC3_REVISION_194A) {
!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) {
if (!dwc->dis_u3_susphy_quirk) {
reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
reg |= DWC3_GUSB3PIPECTL_SUSPHY;
@@ -1004,20 +1007,20 @@ static int dwc3_core_init(struct dwc3 *dwc)
* the DWC_usb3 controller. It is NOT available in the
* DWC_usb31 controller.
*/
if (!dwc3_is_usb31(dwc) && dwc->revision >= DWC3_REVISION_310A) {
if (DWC3_VER_IS_WITHIN(DWC3, 310A, ANY)) {
reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
reg |= DWC3_GUCTL2_RST_ACTBITLATER;
dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
}
if (dwc->revision >= DWC3_REVISION_250A) {
if (!DWC3_VER_IS_PRIOR(DWC3, 250A)) {
reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
/*
* Enable hardware control of sending remote wakeup
* in HS when the device is in the L1 state.
*/
if (dwc->revision >= DWC3_REVISION_290A)
if (!DWC3_VER_IS_PRIOR(DWC3, 290A))
reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW;
if (dwc->dis_tx_ipgap_linecheck_quirk)
@@ -1049,7 +1052,7 @@ static int dwc3_core_init(struct dwc3 *dwc)
* Must config both number of packets and max burst settings to enable
* RX and/or TX threshold.
*/
if (dwc3_is_usb31(dwc) && dwc->dr_mode == USB_DR_MODE_HOST) {
if (!DWC3_IP_IS(DWC3) && dwc->dr_mode == USB_DR_MODE_HOST) {
u8 rx_thr_num = dwc->rx_thr_num_pkt_prd;
u8 rx_maxburst = dwc->rx_max_burst_prd;
u8 tx_thr_num = dwc->tx_thr_num_pkt_prd;
@@ -1371,10 +1374,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
/* check whether the core supports IMOD */
bool dwc3_has_imod(struct dwc3 *dwc)
{
return ((dwc3_is_usb3(dwc) &&
dwc->revision >= DWC3_REVISION_300A) ||
(dwc3_is_usb31(dwc) &&
dwc->revision >= DWC3_USB31_REVISION_120A));
return DWC3_VER_IS_WITHIN(DWC3, 300A, ANY) ||
DWC3_VER_IS_WITHIN(DWC31, 120A, ANY) ||
DWC3_IP_IS(DWC32);
}
static void dwc3_check_params(struct dwc3 *dwc)
@@ -1395,7 +1397,7 @@ static void dwc3_check_params(struct dwc3 *dwc)
* affected version.
*/
if (!dwc->imod_interval &&
(dwc->revision == DWC3_REVISION_300A))
DWC3_VER_IS(DWC3, 300A))
dwc->imod_interval = 1;
/* Check the maximum_speed parameter */
@@ -1417,7 +1419,7 @@ static void dwc3_check_params(struct dwc3 *dwc)
/*
* default to superspeed plus if we are capable.
*/
if (dwc3_is_usb31(dwc) &&
if ((DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) &&
(DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
dwc->maximum_speed = USB_SPEED_SUPER_PLUS;

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* core.h - DesignWare USB3 DRD Core Header
*
@@ -69,6 +69,7 @@
#define DWC3_GEVNTCOUNT_EHB BIT(31)
#define DWC3_GSNPSID_MASK 0xffff0000
#define DWC3_GSNPSREV_MASK 0xffff
#define DWC3_GSNPS_ID(p) (((p) & DWC3_GSNPSID_MASK) >> 16)
/* DWC3 registers memory space boundries */
#define DWC3_XHCI_REGS_START 0x0
@@ -365,6 +366,9 @@
#define DWC3_GHWPARAMS6_SRPSUPPORT BIT(10)
#define DWC3_GHWPARAMS6_EN_FPGA BIT(7)
/* DWC_usb32 only */
#define DWC3_GHWPARAMS6_MDWIDTH(n) ((n) & (0x3 << 8))
/* Global HWPARAMS7 Register */
#define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff)
#define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff)
@@ -491,6 +495,7 @@
#define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09
#define DWC3_DGCMD_ALL_FIFO_FLUSH 0x0a
#define DWC3_DGCMD_SET_ENDPOINT_NRDY 0x0c
#define DWC3_DGCMD_SET_ENDPOINT_PRIME 0x0d
#define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK 0x10
#define DWC3_DGCMD_STATUS(n) (((n) >> 12) & 0x0F)
@@ -697,6 +702,10 @@ struct dwc3_ep {
#define DWC3_EP_END_TRANSFER_PENDING BIT(4)
#define DWC3_EP_PENDING_REQUEST BIT(5)
#define DWC3_EP_DELAY_START BIT(6)
#define DWC3_EP_WAIT_TRANSFER_COMPLETE BIT(7)
#define DWC3_EP_IGNORE_NEXT_NOSTREAM BIT(8)
#define DWC3_EP_FORCE_RESTART_STREAM BIT(9)
#define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
/* This last one is specific to EP0 */
#define DWC3_EP0_DIR_IN BIT(31)
@@ -949,7 +958,8 @@ struct dwc3_scratchpad_array {
* @nr_scratch: number of scratch buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
* @revision: revision register contents
* @ip: controller's ID
* @revision: controller's version of an IP
* @version_type: VERSIONTYPE register contents, a sub release of a revision
* @dr_mode: requested mode of operation
* @current_dr_role: current role of operation when in dual-role mode
@@ -1110,15 +1120,15 @@ struct dwc3 {
u32 u1u2;
u32 maximum_speed;
/*
* All 3.1 IP version constants are greater than the 3.0 IP
* version constants. This works for most version checks in
* dwc3. However, in the future, this may not apply as
* features may be developed on newer versions of the 3.0 IP
* that are not in the 3.1 IP.
*/
u32 ip;
#define DWC3_IP 0x5533
#define DWC31_IP 0x3331
#define DWC32_IP 0x3332
u32 revision;
#define DWC3_REVISION_ANY 0x0
#define DWC3_REVISION_173A 0x5533173a
#define DWC3_REVISION_175A 0x5533175a
#define DWC3_REVISION_180A 0x5533180a
@@ -1143,20 +1153,20 @@ struct dwc3 {
#define DWC3_REVISION_310A 0x5533310a
#define DWC3_REVISION_330A 0x5533330a
/*
* NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really
* just so dwc31 revisions are always larger than dwc3.
*/
#define DWC3_REVISION_IS_DWC31 0x80000000
#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31)
#define DWC3_USB31_REVISION_120A (0x3132302a | DWC3_REVISION_IS_DWC31)
#define DWC3_USB31_REVISION_160A (0x3136302a | DWC3_REVISION_IS_DWC31)
#define DWC3_USB31_REVISION_170A (0x3137302a | DWC3_REVISION_IS_DWC31)
#define DWC3_USB31_REVISION_180A (0x3138302a | DWC3_REVISION_IS_DWC31)
#define DWC3_USB31_REVISION_190A (0x3139302a | DWC3_REVISION_IS_DWC31)
#define DWC31_REVISION_ANY 0x0
#define DWC31_REVISION_110A 0x3131302a
#define DWC31_REVISION_120A 0x3132302a
#define DWC31_REVISION_160A 0x3136302a
#define DWC31_REVISION_170A 0x3137302a
#define DWC31_REVISION_180A 0x3138302a
#define DWC31_REVISION_190A 0x3139302a
#define DWC32_REVISION_ANY 0x0
#define DWC32_REVISION_100A 0x3130302a
u32 version_type;
#define DWC31_VERSIONTYPE_ANY 0x0
#define DWC31_VERSIONTYPE_EA01 0x65613031
#define DWC31_VERSIONTYPE_EA02 0x65613032
#define DWC31_VERSIONTYPE_EA03 0x65613033
@@ -1298,6 +1308,10 @@ struct dwc3_event_depevt {
#define DEPEVT_STREAMEVT_FOUND 1
#define DEPEVT_STREAMEVT_NOTFOUND 2
/* Stream event parameter */
#define DEPEVT_STREAM_PRIME 0xfffe
#define DEPEVT_STREAM_NOSTREAM 0x0
/* Control-only Status */
#define DEPEVT_STATUS_CONTROL_DATA 1
#define DEPEVT_STATUS_CONTROL_STATUS 2
@@ -1400,17 +1414,26 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode);
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
/* check whether we are on the DWC_usb3 core */
static inline bool dwc3_is_usb3(struct dwc3 *dwc)
{
return !(dwc->revision & DWC3_REVISION_IS_DWC31);
}
#define DWC3_IP_IS(_ip) \
(dwc->ip == _ip##_IP)
/* check whether we are on the DWC_usb31 core */
static inline bool dwc3_is_usb31(struct dwc3 *dwc)
{
return !!(dwc->revision & DWC3_REVISION_IS_DWC31);
}
#define DWC3_VER_IS(_ip, _ver) \
(DWC3_IP_IS(_ip) && dwc->revision == _ip##_REVISION_##_ver)
#define DWC3_VER_IS_PRIOR(_ip, _ver) \
(DWC3_IP_IS(_ip) && dwc->revision < _ip##_REVISION_##_ver)
#define DWC3_VER_IS_WITHIN(_ip, _from, _to) \
(DWC3_IP_IS(_ip) && \
dwc->revision >= _ip##_REVISION_##_from && \
(!(_ip##_REVISION_##_to) || \
dwc->revision <= _ip##_REVISION_##_to))
#define DWC3_VER_TYPE_IS_WITHIN(_ip, _ver, _from, _to) \
(DWC3_VER_IS(_ip, _ver) && \
dwc->version_type >= _ip##_VERSIONTYPE_##_from && \
(!(_ip##_VERSIONTYPE_##_to) || \
dwc->version_type <= _ip##_VERSIONTYPE_##_to))
bool dwc3_has_imod(struct dwc3 *dwc);

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/**
* debug.h - DesignWare USB3 DRD Controller Debug Header
*
@@ -68,6 +68,8 @@ dwc3_gadget_generic_cmd_string(u8 cmd)
return "All FIFO Flush";
case DWC3_DGCMD_SET_ENDPOINT_NRDY:
return "Set Endpoint NRDY";
case DWC3_DGCMD_SET_ENDPOINT_PRIME:
return "Set Endpoint Prime";
case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK:
return "Run SoC Bus Loopback Test";
default:

View File

@@ -635,13 +635,18 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused)
struct dwc3_ep *dep = s->private;
struct dwc3 *dwc = dep->dwc;
unsigned long flags;
int mdwidth;
u32 val;
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_TXFIFO);
/* Convert to bytes */
val *= DWC3_MDWIDTH(dwc->hwparams.hwparams0);
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
if (DWC3_IP_IS(DWC32))
mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
val *= mdwidth;
val >>= 3;
seq_printf(s, "%u\n", val);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -654,13 +659,18 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused)
struct dwc3_ep *dep = s->private;
struct dwc3 *dwc = dep->dwc;
unsigned long flags;
int mdwidth;
u32 val;
spin_lock_irqsave(&dwc->lock, flags);
val = dwc3_core_fifo_space(dep, DWC3_RXFIFO);
/* Convert to bytes */
val *= DWC3_MDWIDTH(dwc->hwparams.hwparams0);
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
if (DWC3_IP_IS(DWC32))
mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
val *= mdwidth;
val >>= 3;
seq_printf(s, "%u\n", val);
spin_unlock_irqrestore(&dwc->lock, flags);

View File

@@ -56,7 +56,7 @@ static irqreturn_t dwc3_otg_thread_irq(int irq, void *_dwc)
spin_lock(&dwc->lock);
if (dwc->otg_restart_host) {
dwc3_otg_host_init(dwc);
dwc->otg_restart_host = 0;
dwc->otg_restart_host = false;
}
spin_unlock(&dwc->lock);
@@ -82,7 +82,7 @@ static irqreturn_t dwc3_otg_irq(int irq, void *_dwc)
if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST &&
!(reg & DWC3_OEVT_DEVICEMODE))
dwc->otg_restart_host = 1;
dwc->otg_restart_host = true;
dwc3_writel(dwc->regs, DWC3_OEVT, reg);
ret = IRQ_WAKE_THREAD;
}
@@ -653,6 +653,6 @@ void dwc3_drd_exit(struct dwc3 *dwc)
break;
}
if (!dwc->edev)
if (dwc->otg_irq)
free_irq(dwc->otg_irq, dwc);
}

View File

@@ -14,6 +14,7 @@
#include <linux/dma-mapping.h>
#include <linux/io.h>
#include <linux/of_platform.h>
#include <linux/phy/phy.h>
#include <linux/pm_runtime.h>
/* USBSS register offsets */
@@ -34,6 +35,7 @@
struct dwc3_keystone {
struct device *dev;
void __iomem *usbss;
struct phy *usb3_phy;
};
static inline u32 kdwc3_readl(void __iomem *base, u32 offset)
@@ -95,8 +97,38 @@ static int kdwc3_probe(struct platform_device *pdev)
if (IS_ERR(kdwc->usbss))
return PTR_ERR(kdwc->usbss);
pm_runtime_enable(kdwc->dev);
/* PSC dependency on AM65 needs SERDES0 to be powered before USB0 */
kdwc->usb3_phy = devm_phy_optional_get(dev, "usb3-phy");
if (IS_ERR(kdwc->usb3_phy)) {
error = PTR_ERR(kdwc->usb3_phy);
if (error != -EPROBE_DEFER)
dev_err(dev, "couldn't get usb3 phy: %d\n", error);
return error;
}
phy_pm_runtime_get_sync(kdwc->usb3_phy);
error = phy_reset(kdwc->usb3_phy);
if (error < 0) {
dev_err(dev, "usb3 phy reset failed: %d\n", error);
return error;
}
error = phy_init(kdwc->usb3_phy);
if (error < 0) {
dev_err(dev, "usb3 phy init failed: %d\n", error);
return error;
}
error = phy_power_on(kdwc->usb3_phy);
if (error < 0) {
dev_err(dev, "usb3 phy power on failed: %d\n", error);
phy_exit(kdwc->usb3_phy);
return error;
}
pm_runtime_enable(kdwc->dev);
error = pm_runtime_get_sync(kdwc->dev);
if (error < 0) {
dev_err(kdwc->dev, "pm_runtime_get_sync failed, error %d\n",
@@ -138,6 +170,9 @@ err_core:
err_irq:
pm_runtime_put_sync(kdwc->dev);
pm_runtime_disable(kdwc->dev);
phy_power_off(kdwc->usb3_phy);
phy_exit(kdwc->usb3_phy);
phy_pm_runtime_put_sync(kdwc->usb3_phy);
return error;
}
@@ -163,6 +198,10 @@ static int kdwc3_remove(struct platform_device *pdev)
pm_runtime_put_sync(kdwc->dev);
pm_runtime_disable(kdwc->dev);
phy_power_off(kdwc->usb3_phy);
phy_exit(kdwc->usb3_phy);
phy_pm_runtime_put_sync(kdwc->usb3_phy);
platform_set_drvdata(pdev, NULL);
return 0;

View File

@@ -30,7 +30,7 @@
#include <linux/usb/role.h>
#include <linux/regulator/consumer.h>
/* USB2 Ports Control Registers */
/* USB2 Ports Control Registers, offsets are per-port */
#define U2P_REG_SIZE 0x20
@@ -50,14 +50,16 @@
/* USB Glue Control Registers */
#define USB_R0 0x80
#define G12A_GLUE_OFFSET 0x80
#define USB_R0 0x00
#define USB_R0_P30_LANE0_TX2RX_LOOPBACK BIT(17)
#define USB_R0_P30_LANE0_EXT_PCLK_REQ BIT(18)
#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK GENMASK(28, 19)
#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK GENMASK(30, 29)
#define USB_R0_U2D_ACT BIT(31)
#define USB_R1 0x84
#define USB_R1 0x04
#define USB_R1_U3H_BIGENDIAN_GS BIT(0)
#define USB_R1_U3H_PME_ENABLE BIT(1)
#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK GENMASK(4, 2)
@@ -69,23 +71,23 @@
#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK GENMASK(24, 19)
#define USB_R1_P30_PCS_TX_SWING_FULL_MASK GENMASK(31, 25)
#define USB_R2 0x88
#define USB_R2 0x08
#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(25, 20)
#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK GENMASK(31, 26)
#define USB_R3 0x8c
#define USB_R3 0x0c
#define USB_R3_P30_SSC_ENABLE BIT(0)
#define USB_R3_P30_SSC_RANGE_MASK GENMASK(3, 1)
#define USB_R3_P30_SSC_REF_CLK_SEL_MASK GENMASK(12, 4)
#define USB_R3_P30_REF_SSP_EN BIT(13)
#define USB_R4 0x90
#define USB_R4 0x10
#define USB_R4_P21_PORT_RESET_0 BIT(0)
#define USB_R4_P21_SLEEP_M0 BIT(1)
#define USB_R4_MEM_PD_MASK GENMASK(3, 2)
#define USB_R4_P21_ONLY BIT(4)
#define USB_R5 0x94
#define USB_R5 0x14
#define USB_R5_ID_DIG_SYNC BIT(0)
#define USB_R5_ID_DIG_REG BIT(1)
#define USB_R5_ID_DIG_CFG_MASK GENMASK(3, 2)
@@ -96,15 +98,12 @@
#define USB_R5_ID_DIG_TH_MASK GENMASK(15, 8)
#define USB_R5_ID_DIG_CNT_MASK GENMASK(23, 16)
enum {
USB2_HOST_PHY = 0,
USB2_OTG_PHY,
USB3_HOST_PHY,
PHY_COUNT,
};
#define PHY_COUNT 3
#define USB2_OTG_PHY 1
static const char *phy_names[PHY_COUNT] = {
"usb2-phy0", "usb2-phy1", "usb3-phy0",
static struct clk_bulk_data meson_gxl_clocks[] = {
{ .id = "usb_ctrl" },
{ .id = "ddr" },
};
static struct clk_bulk_data meson_g12a_clocks[] = {
@@ -117,27 +116,133 @@ static struct clk_bulk_data meson_a1_clocks[] = {
{ .id = "xtal_usb_ctrl" },
};
static const char *meson_gxm_phy_names[] = {
"usb2-phy0", "usb2-phy1", "usb2-phy2",
};
static const char *meson_g12a_phy_names[] = {
"usb2-phy0", "usb2-phy1", "usb3-phy0",
};
/*
* Amlogic A1 has a single physical PHY, in slot 1, but still has the
* two U2 PHY controls register blocks like G12A.
* Handling the first PHY on slot 1 would need a large amount of code
* changes, and the current management is generic enough to handle it
* correctly when only the "usb2-phy1" phy is specified on-par with the
* DT bindings.
*/
static const char *meson_a1_phy_names[] = {
"usb2-phy0", "usb2-phy1"
};
struct dwc3_meson_g12a;
struct dwc3_meson_g12a_drvdata {
bool otg_switch_supported;
bool otg_phy_host_port_disable;
struct clk_bulk_data *clks;
int num_clks;
const char **phy_names;
int num_phys;
int (*setup_regmaps)(struct dwc3_meson_g12a *priv, void __iomem *base);
int (*usb2_init_phy)(struct dwc3_meson_g12a *priv, int i,
enum phy_mode mode);
int (*set_phy_mode)(struct dwc3_meson_g12a *priv, int i,
enum phy_mode mode);
int (*usb_init)(struct dwc3_meson_g12a *priv);
int (*usb_post_init)(struct dwc3_meson_g12a *priv);
};
static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv,
void __iomem *base);
static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
void __iomem *base);
static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
enum phy_mode mode);
static int dwc3_meson_gxl_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
enum phy_mode mode);
static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv,
int i, enum phy_mode mode);
static int dwc3_meson_gxl_set_phy_mode(struct dwc3_meson_g12a *priv,
int i, enum phy_mode mode);
static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv);
static int dwc3_meson_gxl_usb_init(struct dwc3_meson_g12a *priv);
static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv);
/*
* For GXL and GXM SoCs:
* USB Phy muxing between the DWC2 Device controller and the DWC3 Host
* controller is buggy when switching from Device to Host when USB port
* is unpopulated, it causes the DWC3 to hard crash.
* When populated (including OTG switching with ID pin), the switch works
* like a charm like on the G12A platforms.
* In order to still switch from Host to Device on an USB Type-A port,
* an U2_PORT_DISABLE bit has been added to disconnect the DWC3 Host
* controller from the port, but when used the DWC3 controller must be
* reset to recover usage of the port.
*/
static struct dwc3_meson_g12a_drvdata gxl_drvdata = {
.otg_switch_supported = true,
.otg_phy_host_port_disable = true,
.clks = meson_gxl_clocks,
.num_clks = ARRAY_SIZE(meson_g12a_clocks),
.phy_names = meson_a1_phy_names,
.num_phys = ARRAY_SIZE(meson_a1_phy_names),
.setup_regmaps = dwc3_meson_gxl_setup_regmaps,
.usb2_init_phy = dwc3_meson_gxl_usb2_init_phy,
.set_phy_mode = dwc3_meson_gxl_set_phy_mode,
.usb_init = dwc3_meson_gxl_usb_init,
.usb_post_init = dwc3_meson_gxl_usb_post_init,
};
static struct dwc3_meson_g12a_drvdata gxm_drvdata = {
.otg_switch_supported = true,
.otg_phy_host_port_disable = true,
.clks = meson_gxl_clocks,
.num_clks = ARRAY_SIZE(meson_g12a_clocks),
.phy_names = meson_gxm_phy_names,
.num_phys = ARRAY_SIZE(meson_gxm_phy_names),
.setup_regmaps = dwc3_meson_gxl_setup_regmaps,
.usb2_init_phy = dwc3_meson_gxl_usb2_init_phy,
.set_phy_mode = dwc3_meson_gxl_set_phy_mode,
.usb_init = dwc3_meson_gxl_usb_init,
.usb_post_init = dwc3_meson_gxl_usb_post_init,
};
static struct dwc3_meson_g12a_drvdata g12a_drvdata = {
.otg_switch_supported = true,
.clks = meson_g12a_clocks,
.num_clks = ARRAY_SIZE(meson_g12a_clocks),
.phy_names = meson_g12a_phy_names,
.num_phys = ARRAY_SIZE(meson_g12a_phy_names),
.setup_regmaps = dwc3_meson_g12a_setup_regmaps,
.usb2_init_phy = dwc3_meson_g12a_usb2_init_phy,
.set_phy_mode = dwc3_meson_g12a_set_phy_mode,
.usb_init = dwc3_meson_g12a_usb_init,
};
static struct dwc3_meson_g12a_drvdata a1_drvdata = {
.otg_switch_supported = false,
.clks = meson_a1_clocks,
.num_clks = ARRAY_SIZE(meson_a1_clocks),
.phy_names = meson_a1_phy_names,
.num_phys = ARRAY_SIZE(meson_a1_phy_names),
.setup_regmaps = dwc3_meson_g12a_setup_regmaps,
.usb2_init_phy = dwc3_meson_g12a_usb2_init_phy,
.set_phy_mode = dwc3_meson_g12a_set_phy_mode,
.usb_init = dwc3_meson_g12a_usb_init,
};
struct dwc3_meson_g12a {
struct device *dev;
struct regmap *regmap;
struct regmap *u2p_regmap[PHY_COUNT];
struct regmap *usb_glue_regmap;
struct reset_control *reset;
struct phy *phys[PHY_COUNT];
enum usb_dr_mode otg_mode;
@@ -150,49 +255,78 @@ struct dwc3_meson_g12a {
const struct dwc3_meson_g12a_drvdata *drvdata;
};
static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv,
int i, enum phy_mode mode)
static int dwc3_meson_gxl_set_phy_mode(struct dwc3_meson_g12a *priv,
int i, enum phy_mode mode)
{
return phy_set_mode(priv->phys[i], mode);
}
static int dwc3_meson_gxl_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
enum phy_mode mode)
{
/* On GXL PHY must be started in device mode for DWC2 init */
return priv->drvdata->set_phy_mode(priv, i,
(i == USB2_OTG_PHY) ? PHY_MODE_USB_DEVICE
: PHY_MODE_USB_HOST);
}
static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv,
int i, enum phy_mode mode)
{
if (mode == PHY_MODE_USB_HOST)
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
U2P_R0_HOST_DEVICE,
U2P_R0_HOST_DEVICE);
else
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
U2P_R0_HOST_DEVICE, 0);
return 0;
}
static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
enum phy_mode mode)
{
int i;
int ret;
if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
priv->otg_phy_mode = PHY_MODE_USB_DEVICE;
else
priv->otg_phy_mode = PHY_MODE_USB_HOST;
regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
U2P_R0_POWER_ON_RESET,
U2P_R0_POWER_ON_RESET);
for (i = 0 ; i < USB3_HOST_PHY ; ++i) {
if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) {
regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
ret = priv->drvdata->set_phy_mode(priv, i, mode);
} else
ret = priv->drvdata->set_phy_mode(priv, i,
PHY_MODE_USB_HOST);
if (ret)
return ret;
regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
U2P_R0_POWER_ON_RESET, 0);
return 0;
}
static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv,
enum phy_mode mode)
{
int i, ret;
for (i = 0; i < priv->drvdata->num_phys; ++i) {
if (!priv->phys[i])
continue;
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
U2P_R0_POWER_ON_RESET,
U2P_R0_POWER_ON_RESET);
if (!strstr(priv->drvdata->phy_names[i], "usb2"))
continue;
if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) {
regmap_update_bits(priv->regmap,
U2P_R0 + (U2P_REG_SIZE * i),
U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
dwc3_meson_g12a_usb2_set_mode(priv, i,
priv->otg_phy_mode);
} else
dwc3_meson_g12a_usb2_set_mode(priv, i,
PHY_MODE_USB_HOST);
regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
U2P_R0_POWER_ON_RESET, 0);
ret = priv->drvdata->usb2_init_phy(priv, i, mode);
if (ret)
return ret;
}
return 0;
@@ -200,7 +334,7 @@ static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
{
regmap_update_bits(priv->regmap, USB_R3,
regmap_update_bits(priv->usb_glue_regmap, USB_R3,
USB_R3_P30_SSC_RANGE_MASK |
USB_R3_P30_REF_SSP_EN,
USB_R3_P30_SSC_ENABLE |
@@ -208,61 +342,77 @@ static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
USB_R3_P30_REF_SSP_EN);
udelay(2);
regmap_update_bits(priv->regmap, USB_R2,
regmap_update_bits(priv->usb_glue_regmap, USB_R2,
USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK,
FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15));
regmap_update_bits(priv->regmap, USB_R2,
regmap_update_bits(priv->usb_glue_regmap, USB_R2,
USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK,
FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20));
udelay(2);
regmap_update_bits(priv->regmap, USB_R1,
regmap_update_bits(priv->usb_glue_regmap, USB_R1,
USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT,
USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT);
regmap_update_bits(priv->regmap, USB_R1,
regmap_update_bits(priv->usb_glue_regmap, USB_R1,
USB_R1_P30_PCS_TX_SWING_FULL_MASK,
FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127));
}
static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv)
static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv,
enum phy_mode mode)
{
if (priv->otg_phy_mode == PHY_MODE_USB_DEVICE) {
regmap_update_bits(priv->regmap, USB_R0,
if (mode == PHY_MODE_USB_DEVICE) {
if (priv->otg_mode != USB_DR_MODE_OTG &&
priv->drvdata->otg_phy_host_port_disable)
/* Isolate the OTG PHY port from the Host Controller */
regmap_update_bits(priv->usb_glue_regmap, USB_R1,
USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK,
FIELD_PREP(USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK,
BIT(USB2_OTG_PHY)));
regmap_update_bits(priv->usb_glue_regmap, USB_R0,
USB_R0_U2D_ACT, USB_R0_U2D_ACT);
regmap_update_bits(priv->regmap, USB_R0,
regmap_update_bits(priv->usb_glue_regmap, USB_R0,
USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0);
regmap_update_bits(priv->regmap, USB_R4,
regmap_update_bits(priv->usb_glue_regmap, USB_R4,
USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0);
} else {
regmap_update_bits(priv->regmap, USB_R0,
if (priv->otg_mode != USB_DR_MODE_OTG &&
priv->drvdata->otg_phy_host_port_disable) {
regmap_update_bits(priv->usb_glue_regmap, USB_R1,
USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK, 0);
msleep(500);
}
regmap_update_bits(priv->usb_glue_regmap, USB_R0,
USB_R0_U2D_ACT, 0);
regmap_update_bits(priv->regmap, USB_R4,
regmap_update_bits(priv->usb_glue_regmap, USB_R4,
USB_R4_P21_SLEEP_M0, 0);
}
}
static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
static int dwc3_meson_g12a_usb_init_glue(struct dwc3_meson_g12a *priv,
enum phy_mode mode)
{
int ret;
ret = dwc3_meson_g12a_usb2_init(priv);
ret = dwc3_meson_g12a_usb2_init(priv, mode);
if (ret)
return ret;
regmap_update_bits(priv->regmap, USB_R1,
regmap_update_bits(priv->usb_glue_regmap, USB_R1,
USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
regmap_update_bits(priv->regmap, USB_R5,
regmap_update_bits(priv->usb_glue_regmap, USB_R5,
USB_R5_ID_DIG_EN_0,
USB_R5_ID_DIG_EN_0);
regmap_update_bits(priv->regmap, USB_R5,
regmap_update_bits(priv->usb_glue_regmap, USB_R5,
USB_R5_ID_DIG_EN_1,
USB_R5_ID_DIG_EN_1);
regmap_update_bits(priv->regmap, USB_R5,
regmap_update_bits(priv->usb_glue_regmap, USB_R5,
USB_R5_ID_DIG_TH_MASK,
FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
@@ -270,12 +420,13 @@ static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
if (priv->usb3_ports)
dwc3_meson_g12a_usb3_init(priv);
dwc3_meson_g12a_usb_otg_apply_mode(priv);
dwc3_meson_g12a_usb_otg_apply_mode(priv, mode);
return 0;
}
static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = {
static const struct regmap_config phy_meson_g12a_usb_glue_regmap_conf = {
.name = "usb-glue",
.reg_bits = 8,
.val_bits = 32,
.reg_stride = 4,
@@ -284,17 +435,19 @@ static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = {
static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
{
const char *phy_name;
int i;
for (i = 0 ; i < PHY_COUNT ; ++i) {
priv->phys[i] = devm_phy_optional_get(priv->dev, phy_names[i]);
for (i = 0 ; i < priv->drvdata->num_phys ; ++i) {
phy_name = priv->drvdata->phy_names[i];
priv->phys[i] = devm_phy_optional_get(priv->dev, phy_name);
if (!priv->phys[i])
continue;
if (IS_ERR(priv->phys[i]))
return PTR_ERR(priv->phys[i]);
if (i == USB3_HOST_PHY)
if (strstr(phy_name, "usb3"))
priv->usb3_ports++;
else
priv->usb2_ports++;
@@ -310,7 +463,7 @@ static enum phy_mode dwc3_meson_g12a_get_id(struct dwc3_meson_g12a *priv)
{
u32 reg;
regmap_read(priv->regmap, USB_R5, &reg);
regmap_read(priv->usb_glue_regmap, USB_R5, &reg);
if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG))
return PHY_MODE_USB_DEVICE;
@@ -342,9 +495,11 @@ static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv,
priv->otg_phy_mode = mode;
dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode);
ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY, mode);
if (ret)
return ret;
dwc3_meson_g12a_usb_otg_apply_mode(priv);
dwc3_meson_g12a_usb_otg_apply_mode(priv, mode);
return 0;
}
@@ -364,6 +519,13 @@ static int dwc3_meson_g12a_role_set(struct usb_role_switch *sw,
if (mode == priv->otg_phy_mode)
return 0;
if (priv->drvdata->otg_phy_host_port_disable)
dev_warn_once(priv->dev, "Manual OTG switch is broken on this "\
"SoC, when manual switching from "\
"Host to device, DWC3 controller "\
"will need to be resetted in order "\
"to recover usage of the Host port");
return dwc3_meson_g12a_otg_mode_set(priv, mode);
}
@@ -386,7 +548,8 @@ static irqreturn_t dwc3_meson_g12a_irq_thread(int irq, void *data)
dev_warn(priv->dev, "Failed to switch OTG mode\n");
}
regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
regmap_update_bits(priv->usb_glue_regmap, USB_R5,
USB_R5_ID_DIG_IRQ, 0);
return IRQ_HANDLED;
}
@@ -421,7 +584,7 @@ static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
if (priv->otg_mode == USB_DR_MODE_OTG) {
/* Ack irq before registering */
regmap_update_bits(priv->regmap, USB_R5,
regmap_update_bits(priv->usb_glue_regmap, USB_R5,
USB_R5_ID_DIG_IRQ, 0);
irq = platform_get_irq(pdev, 0);
@@ -457,6 +620,77 @@ static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
return 0;
}
static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv,
void __iomem *base)
{
/* GXL controls the PHY mode in the PHY registers unlike G12A */
priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev, base,
&phy_meson_g12a_usb_glue_regmap_conf);
if (IS_ERR(priv->usb_glue_regmap))
return PTR_ERR(priv->usb_glue_regmap);
return 0;
}
static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
void __iomem *base)
{
int i;
priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev,
base + G12A_GLUE_OFFSET,
&phy_meson_g12a_usb_glue_regmap_conf);
if (IS_ERR(priv->usb_glue_regmap))
return PTR_ERR(priv->usb_glue_regmap);
/* Create a regmap for each USB2 PHY control register set */
for (i = 0; i < priv->usb2_ports; i++) {
struct regmap_config u2p_regmap_config = {
.reg_bits = 8,
.val_bits = 32,
.reg_stride = 4,
.max_register = U2P_R1,
};
u2p_regmap_config.name = devm_kasprintf(priv->dev, GFP_KERNEL,
"u2p-%d", i);
if (!u2p_regmap_config.name)
return -ENOMEM;
priv->u2p_regmap[i] = devm_regmap_init_mmio(priv->dev,
base + (i * U2P_REG_SIZE),
&u2p_regmap_config);
if (IS_ERR(priv->u2p_regmap[i]))
return PTR_ERR(priv->u2p_regmap[i]);
}
return 0;
}
static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
{
return dwc3_meson_g12a_usb_init_glue(priv, priv->otg_phy_mode);
}
static int dwc3_meson_gxl_usb_init(struct dwc3_meson_g12a *priv)
{
return dwc3_meson_g12a_usb_init_glue(priv, PHY_MODE_USB_DEVICE);
}
static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv)
{
int ret;
ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY,
priv->otg_phy_mode);
if (ret)
return ret;
dwc3_meson_g12a_usb_otg_apply_mode(priv, priv->otg_phy_mode);
return 0;
}
static int dwc3_meson_g12a_probe(struct platform_device *pdev)
{
struct dwc3_meson_g12a *priv;
@@ -473,10 +707,8 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
if (IS_ERR(base))
return PTR_ERR(base);
priv->regmap = devm_regmap_init_mmio(dev, base,
&phy_meson_g12a_usb3_regmap_conf);
if (IS_ERR(priv->regmap))
return PTR_ERR(priv->regmap);
priv->drvdata = of_device_get_match_data(&pdev->dev);
priv->dev = dev;
priv->vbus = devm_regulator_get_optional(dev, "vbus");
if (IS_ERR(priv->vbus)) {
@@ -485,8 +717,6 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
priv->vbus = NULL;
}
priv->drvdata = of_device_get_match_data(&pdev->dev);
ret = devm_clk_bulk_get(dev,
priv->drvdata->num_clks,
priv->drvdata->clks);
@@ -499,13 +729,12 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
return ret;
platform_set_drvdata(pdev, priv);
priv->dev = dev;
priv->reset = devm_reset_control_get(dev, NULL);
priv->reset = devm_reset_control_get_shared(dev, NULL);
if (IS_ERR(priv->reset)) {
ret = PTR_ERR(priv->reset);
dev_err(dev, "failed to get device reset, err=%d\n", ret);
return ret;
goto err_disable_clks;
}
ret = reset_control_reset(priv->reset);
@@ -516,6 +745,10 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
if (ret)
goto err_disable_clks;
ret = priv->drvdata->setup_regmaps(priv, base);
if (ret)
return ret;
if (priv->vbus) {
ret = regulator_enable(priv->vbus);
if (ret)
@@ -525,7 +758,14 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
/* Get dr_mode */
priv->otg_mode = usb_get_dr_mode(dev);
dwc3_meson_g12a_usb_init(priv);
if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
priv->otg_phy_mode = PHY_MODE_USB_DEVICE;
else
priv->otg_phy_mode = PHY_MODE_USB_HOST;
ret = priv->drvdata->usb_init(priv);
if (ret)
goto err_disable_clks;
/* Init PHYs */
for (i = 0 ; i < PHY_COUNT ; ++i) {
@@ -541,6 +781,12 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
goto err_phys_exit;
}
if (priv->drvdata->usb_post_init) {
ret = priv->drvdata->usb_post_init(priv);
if (ret)
goto err_phys_power;
}
ret = of_platform_populate(np, NULL, NULL, dev);
if (ret)
goto err_phys_power;
@@ -642,7 +888,9 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
reset_control_deassert(priv->reset);
dwc3_meson_g12a_usb_init(priv);
ret = priv->drvdata->usb_init(priv);
if (ret)
return ret;
/* Init PHYs */
for (i = 0 ; i < PHY_COUNT ; ++i) {
@@ -674,6 +922,14 @@ static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = {
};
static const struct of_device_id dwc3_meson_g12a_match[] = {
{
.compatible = "amlogic,meson-gxl-usb-ctrl",
.data = &gxl_drvdata,
},
{
.compatible = "amlogic,meson-gxm-usb-ctrl",
.data = &gxm_drvdata,
},
{
.compatible = "amlogic,meson-g12a-usb-ctrl",
.data = &g12a_drvdata,

View File

@@ -27,7 +27,6 @@ struct dwc3_of_simple {
struct clk_bulk_data *clks;
int num_clocks;
struct reset_control *resets;
bool pulse_resets;
bool need_reset;
};
@@ -38,7 +37,6 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node;
int ret;
bool shared_resets = false;
simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
if (!simple)
@@ -54,13 +52,7 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
if (of_device_is_compatible(np, "rockchip,rk3399-dwc3"))
simple->need_reset = true;
if (of_device_is_compatible(np, "amlogic,meson-axg-dwc3") ||
of_device_is_compatible(np, "amlogic,meson-gxl-dwc3")) {
shared_resets = true;
simple->pulse_resets = true;
}
simple->resets = of_reset_control_array_get(np, shared_resets, true,
simple->resets = of_reset_control_array_get(np, false, true,
true);
if (IS_ERR(simple->resets)) {
ret = PTR_ERR(simple->resets);
@@ -68,15 +60,9 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
return ret;
}
if (simple->pulse_resets) {
ret = reset_control_reset(simple->resets);
if (ret)
goto err_resetc_put;
} else {
ret = reset_control_deassert(simple->resets);
if (ret)
goto err_resetc_put;
}
ret = reset_control_deassert(simple->resets);
if (ret)
goto err_resetc_put;
ret = clk_bulk_get_all(simple->dev, &simple->clks);
if (ret < 0)
@@ -102,8 +88,7 @@ err_clk_put:
clk_bulk_put_all(simple->num_clocks, simple->clks);
err_resetc_assert:
if (!simple->pulse_resets)
reset_control_assert(simple->resets);
reset_control_assert(simple->resets);
err_resetc_put:
reset_control_put(simple->resets);
@@ -118,8 +103,7 @@ static void __dwc3_of_simple_teardown(struct dwc3_of_simple *simple)
clk_bulk_put_all(simple->num_clocks, simple->clks);
simple->num_clocks = 0;
if (!simple->pulse_resets)
reset_control_assert(simple->resets);
reset_control_assert(simple->resets);
reset_control_put(simple->resets);
@@ -191,8 +175,6 @@ static const struct of_device_id of_dwc3_simple_match[] = {
{ .compatible = "xlnx,zynqmp-dwc3" },
{ .compatible = "cavium,octeon-7130-usb-uctl" },
{ .compatible = "sprd,sc9860-dwc3" },
{ .compatible = "amlogic,meson-axg-dwc3" },
{ .compatible = "amlogic,meson-gxl-dwc3" },
{ .compatible = "allwinner,sun50i-h6-dwc3" },
{ /* Sentinel */ }
};

View File

@@ -95,7 +95,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
* Wait until device controller is ready. Only applies to 1.94a and
* later RTL.
*/
if (dwc->revision >= DWC3_REVISION_194A) {
if (!DWC3_VER_IS_PRIOR(DWC3, 194A)) {
while (--retries) {
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
if (reg & DWC3_DSTS_DCNRD)
@@ -122,7 +122,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
* The following code is racy when called from dwc3_gadget_wakeup,
* and is not needed, at least on newer versions
*/
if (dwc->revision >= DWC3_REVISION_194A)
if (!DWC3_VER_IS_PRIOR(DWC3, 194A))
return 0;
/* wait for a change in DSTS */
@@ -273,7 +273,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
{
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
u32 timeout = 1000;
u32 timeout = 5000;
u32 saved_config = 0;
u32 reg;
@@ -356,6 +356,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
ret = 0;
break;
case DEPEVT_TRANSFER_NO_RESOURCE:
dev_WARN(dwc->dev, "No resource for %s\n",
dep->name);
ret = -EINVAL;
break;
case DEPEVT_TRANSFER_BUS_EXPIRY:
@@ -387,9 +389,12 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status);
if (ret == 0 && DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
dep->flags |= DWC3_EP_TRANSFER_STARTED;
dwc3_gadget_ep_get_transfer_index(dep);
if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
if (ret == 0)
dep->flags |= DWC3_EP_TRANSFER_STARTED;
if (ret != -ETIMEDOUT)
dwc3_gadget_ep_get_transfer_index(dep);
}
if (saved_config) {
@@ -415,7 +420,8 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
* IN transfers due to a mishandled error condition. Synopsys
* STAR 9000614252.
*/
if (dep->direction && (dwc->revision >= DWC3_REVISION_260A) &&
if (dep->direction &&
!DWC3_VER_IS_PRIOR(DWC3, 260A) &&
(dwc->gadget.speed >= USB_SPEED_SUPER))
cmd |= DWC3_DEPCMD_CLEARPENDIN;
@@ -573,6 +579,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) {
params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE
| DWC3_DEPCFG_XFER_COMPLETE_EN
| DWC3_DEPCFG_STREAM_EVENT_EN;
dep->stream_capable = true;
}
@@ -603,6 +610,9 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, &params);
}
static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
bool interrupt);
/**
* __dwc3_gadget_ep_enable - initializes a hw endpoint
* @dep: endpoint to be initialized
@@ -663,7 +673,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
* Issue StartTransfer here with no-op TRB so we can always rely on No
* Response Update Transfer command.
*/
if ((usb_endpoint_xfer_bulk(desc) && !dep->stream_capable) ||
if (usb_endpoint_xfer_bulk(desc) ||
usb_endpoint_xfer_int(desc)) {
struct dwc3_gadget_ep_cmd_params params;
struct dwc3_trb *trb;
@@ -682,6 +692,29 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
if (ret < 0)
return ret;
if (dep->stream_capable) {
/*
* For streams, at start, there maybe a race where the
* host primes the endpoint before the function driver
* queues a request to initiate a stream. In that case,
* the controller will not see the prime to generate the
* ERDY and start stream. To workaround this, issue a
* no-op TRB as normal, but end it immediately. As a
* result, when the function driver queues the request,
* the next START_TRANSFER command will cause the
* controller to generate an ERDY to initiate the
* stream.
*/
dwc3_stop_active_transfer(dep, true, true);
/*
* All stream eps will reinitiate stream on NoStream
* rejection until we can determine that the host can
* prime after the first transfer.
*/
dep->flags |= DWC3_EP_FORCE_RESTART_STREAM;
}
}
out:
@@ -690,8 +723,6 @@ out:
return 0;
}
static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
bool interrupt);
static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
{
struct dwc3_request *req;
@@ -912,7 +943,8 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
dma_addr_t dma, unsigned length, unsigned chain, unsigned node,
unsigned stream_id, unsigned short_not_ok, unsigned no_interrupt)
unsigned stream_id, unsigned short_not_ok,
unsigned no_interrupt, unsigned is_last)
{
struct dwc3 *dwc = dep->dwc;
struct usb_gadget *gadget = &dwc->gadget;
@@ -1005,6 +1037,8 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
if (chain)
trb->ctrl |= DWC3_TRB_CTRL_CHN;
else if (dep->stream_capable && is_last)
trb->ctrl |= DWC3_TRB_CTRL_LST;
if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id);
@@ -1032,6 +1066,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
unsigned stream_id = req->request.stream_id;
unsigned short_not_ok = req->request.short_not_ok;
unsigned no_interrupt = req->request.no_interrupt;
unsigned is_last = req->request.is_last;
if (req->request.num_sgs > 0) {
length = sg_dma_len(req->start_sg);
@@ -1052,7 +1087,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
req->num_trbs++;
__dwc3_prepare_one_trb(dep, trb, dma, length, chain, node,
stream_id, short_not_ok, no_interrupt);
stream_id, short_not_ok, no_interrupt, is_last);
}
static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
@@ -1097,7 +1132,8 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
maxp - rem, false, 1,
req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt);
req->request.no_interrupt,
req->request.is_last);
} else {
dwc3_prepare_one_trb(dep, req, chain, i);
}
@@ -1141,7 +1177,8 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
false, 1, req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt);
req->request.no_interrupt,
req->request.is_last);
} else if (req->request.zero && req->request.length &&
(IS_ALIGNED(req->request.length, maxp))) {
struct dwc3 *dwc = dep->dwc;
@@ -1158,7 +1195,8 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
__dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
false, 1, req->request.stream_id,
req->request.short_not_ok,
req->request.no_interrupt);
req->request.no_interrupt,
req->request.is_last);
} else {
dwc3_prepare_one_trb(dep, req, false, 0);
}
@@ -1194,6 +1232,14 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
if (!dwc3_calc_trbs_left(dep))
return;
/*
* Don't prepare beyond a transfer. In DWC_usb32, its transfer
* burst capability may try to read and use TRBs beyond the
* active transfer instead of stopping.
*/
if (dep->stream_capable && req->request.is_last)
return;
}
list_for_each_entry_safe(req, n, &dep->pending_list, list) {
@@ -1217,9 +1263,19 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
if (!dwc3_calc_trbs_left(dep))
return;
/*
* Don't prepare beyond a transfer. In DWC_usb32, its transfer
* burst capability may try to read and use TRBs beyond the
* active transfer instead of stopping.
*/
if (dep->stream_capable && req->request.is_last)
return;
}
}
static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep);
static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
{
struct dwc3_gadget_ep_cmd_params params;
@@ -1259,17 +1315,26 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
if (ret < 0) {
/*
* FIXME we need to iterate over the list of requests
* here and stop, unmap, free and del each of the linked
* requests instead of what we do now.
*/
if (req->trb)
memset(req->trb, 0, sizeof(struct dwc3_trb));
dwc3_gadget_del_and_unmap_request(dep, req, ret);
struct dwc3_request *tmp;
if (ret == -EAGAIN)
return ret;
dwc3_stop_active_transfer(dep, true, true);
list_for_each_entry_safe(req, tmp, &dep->started_list, list)
dwc3_gadget_move_cancelled_request(req);
/* If ep isn't started, then there's no end transfer pending */
if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
dwc3_gadget_ep_cleanup_cancelled_requests(dep);
return ret;
}
if (dep->stream_capable && req->request.is_last)
dep->flags |= DWC3_EP_WAIT_TRANSFER_COMPLETE;
return 0;
}
@@ -1402,17 +1467,15 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
int ret;
int i;
if (list_empty(&dep->pending_list)) {
if (list_empty(&dep->pending_list) &&
list_empty(&dep->started_list)) {
dep->flags |= DWC3_EP_PENDING_REQUEST;
return -EAGAIN;
}
if (!dwc->dis_start_transfer_quirk && dwc3_is_usb31(dwc) &&
(dwc->revision <= DWC3_USB31_REVISION_160A ||
(dwc->revision == DWC3_USB31_REVISION_170A &&
dwc->version_type >= DWC31_VERSIONTYPE_EA01 &&
dwc->version_type <= DWC31_VERSIONTYPE_EA06))) {
if (!dwc->dis_start_transfer_quirk &&
(DWC3_VER_IS_PRIOR(DWC31, 170A) ||
DWC3_VER_TYPE_IS_WITHIN(DWC31, 170A, EA01, EA06))) {
if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction)
return dwc3_gadget_start_isoc_quirk(dep);
}
@@ -1425,6 +1488,27 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
break;
}
/*
* After a number of unsuccessful start attempts due to bus-expiry
* status, issue END_TRANSFER command and retry on the next XferNotReady
* event.
*/
if (ret == -EAGAIN) {
struct dwc3_gadget_ep_cmd_params params;
u32 cmd;
cmd = DWC3_DEPCMD_ENDTRANSFER |
DWC3_DEPCMD_CMDIOC |
DWC3_DEPCMD_PARAM(dep->resource_index);
dep->resource_index = 0;
memset(&params, 0, sizeof(params));
ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
if (!ret)
dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
}
return ret;
}
@@ -1457,6 +1541,9 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
list_add_tail(&req->list, &dep->pending_list);
req->status = DWC3_REQUEST_STATUS_QUEUED;
if (dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)
return 0;
/* Start the transfer only after the END_TRANSFER is completed */
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) {
dep->flags |= DWC3_EP_DELAY_START;
@@ -1508,6 +1595,10 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r
{
int i;
/* If req->trb is not set, then the request has not started */
if (!req->trb)
return;
/*
* If request was already started, this means we had to
* stop the transfer. With that we also need to ignore
@@ -1556,39 +1647,40 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
spin_lock_irqsave(&dwc->lock, flags);
list_for_each_entry(r, &dep->pending_list, list) {
list_for_each_entry(r, &dep->cancelled_list, list) {
if (r == req)
break;
goto out;
}
if (r != req) {
list_for_each_entry(r, &dep->started_list, list) {
if (r == req)
break;
}
list_for_each_entry(r, &dep->pending_list, list) {
if (r == req) {
dwc3_gadget_giveback(dep, req, -ECONNRESET);
goto out;
}
}
list_for_each_entry(r, &dep->started_list, list) {
if (r == req) {
struct dwc3_request *t;
/* wait until it is processed */
dwc3_stop_active_transfer(dep, true, true);
if (!r->trb)
goto out0;
/*
* Remove any started request if the transfer is
* cancelled.
*/
list_for_each_entry_safe(r, t, &dep->started_list, list)
dwc3_gadget_move_cancelled_request(r);
dwc3_gadget_move_cancelled_request(req);
if (dep->flags & DWC3_EP_TRANSFER_STARTED)
goto out0;
else
goto out1;
goto out;
}
dev_err(dwc->dev, "request %pK was not queued to %s\n",
request, ep->name);
ret = -EINVAL;
goto out0;
}
out1:
dwc3_gadget_giveback(dep, req, -ECONNRESET);
out0:
dev_err(dwc->dev, "request %pK was not queued to %s\n",
request, ep->name);
ret = -EINVAL;
out:
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -1598,6 +1690,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
{
struct dwc3_gadget_ep_cmd_params params;
struct dwc3 *dwc = dep->dwc;
struct dwc3_request *req;
struct dwc3_request *tmp;
int ret;
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
@@ -1634,13 +1728,37 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
else
dep->flags |= DWC3_EP_STALL;
} else {
/*
* Don't issue CLEAR_STALL command to control endpoints. The
* controller automatically clears the STALL when it receives
* the SETUP token.
*/
if (dep->number <= 1) {
dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
return 0;
}
ret = dwc3_send_clear_stall_ep_cmd(dep);
if (ret)
if (ret) {
dev_err(dwc->dev, "failed to clear STALL on %s\n",
dep->name);
else
dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
return ret;
}
dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
dwc3_stop_active_transfer(dep, true, true);
list_for_each_entry_safe(req, tmp, &dep->started_list, list)
dwc3_gadget_move_cancelled_request(req);
list_for_each_entry_safe(req, tmp, &dep->pending_list, list)
dwc3_gadget_move_cancelled_request(req);
if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) {
dep->flags &= ~DWC3_EP_DELAY_START;
dwc3_gadget_ep_cleanup_cancelled_requests(dep);
}
}
return ret;
@@ -1756,7 +1874,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
}
/* Recent versions do this automatically */
if (dwc->revision < DWC3_REVISION_194A) {
if (DWC3_VER_IS_PRIOR(DWC3, 194A)) {
/* write zeroes to Link Change Request */
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
@@ -1818,12 +1936,12 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
if (is_on) {
if (dwc->revision <= DWC3_REVISION_187A) {
if (DWC3_VER_IS_WITHIN(DWC3, ANY, 187A)) {
reg &= ~DWC3_DCTL_TRGTULST_MASK;
reg |= DWC3_DCTL_TRGTULST_RX_DET;
}
if (dwc->revision >= DWC3_REVISION_194A)
if (!DWC3_VER_IS_PRIOR(DWC3, 194A))
reg &= ~DWC3_DCTL_KEEP_CONNECT;
reg |= DWC3_DCTL_RUN_STOP;
@@ -1897,7 +2015,7 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
DWC3_DEVTEN_USBRSTEN |
DWC3_DEVTEN_DISCONNEVTEN);
if (dwc->revision < DWC3_REVISION_250A)
if (DWC3_VER_IS_PRIOR(DWC3, 250A))
reg |= DWC3_DEVTEN_ULSTCNGEN;
dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
@@ -1942,6 +2060,8 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc)
ram2_depth = DWC3_GHWPARAMS7_RAM2_DEPTH(dwc->hwparams.hwparams7);
mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0);
if (DWC3_IP_IS(DWC32))
mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
nump = ((ram2_depth * mdwidth / 8) - 24 - 16) / 1024;
nump = min_t(u32, nump, 16);
@@ -1978,10 +2098,10 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
* bursts of data without going through any sort of endpoint throttling.
*/
reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
if (dwc3_is_usb31(dwc))
reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
else
if (DWC3_IP_IS(DWC3))
reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
else
reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
@@ -2154,7 +2274,7 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
* STAR#9000525659: Clock Domain Crossing on DCTL in
* USB 2.0 Mode
*/
if (dwc->revision < DWC3_REVISION_220A &&
if (DWC3_VER_IS_PRIOR(DWC3, 220A) &&
!dwc->dis_metastability_quirk) {
reg |= DWC3_DCFG_SUPERSPEED;
} else {
@@ -2172,18 +2292,18 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
reg |= DWC3_DCFG_SUPERSPEED;
break;
case USB_SPEED_SUPER_PLUS:
if (dwc3_is_usb31(dwc))
reg |= DWC3_DCFG_SUPERSPEED_PLUS;
else
if (DWC3_IP_IS(DWC3))
reg |= DWC3_DCFG_SUPERSPEED;
else
reg |= DWC3_DCFG_SUPERSPEED_PLUS;
break;
default:
dev_err(dwc->dev, "invalid speed (%d)\n", speed);
if (dwc->revision & DWC3_REVISION_IS_DWC31)
reg |= DWC3_DCFG_SUPERSPEED_PLUS;
else
if (DWC3_IP_IS(DWC3))
reg |= DWC3_DCFG_SUPERSPEED;
else
reg |= DWC3_DCFG_SUPERSPEED_PLUS;
}
}
dwc3_writel(dwc->regs, DWC3_DCFG, reg);
@@ -2226,14 +2346,17 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
int size;
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
if (DWC3_IP_IS(DWC32))
mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
/* MDWIDTH is represented in bits, we need it in bytes */
mdwidth /= 8;
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1));
if (dwc3_is_usb31(dwc))
size = DWC31_GTXFIFOSIZ_TXFDEP(size);
else
if (DWC3_IP_IS(DWC3))
size = DWC3_GTXFIFOSIZ_TXFDEP(size);
else
size = DWC31_GTXFIFOSIZ_TXFDEP(size);
/* FIFO Depth is in MDWDITH bytes. Multiply */
size *= mdwidth;
@@ -2270,16 +2393,18 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
int size;
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
if (DWC3_IP_IS(DWC32))
mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
/* MDWIDTH is represented in bits, convert to bytes */
mdwidth /= 8;
/* All OUT endpoints share a single RxFIFO space */
size = dwc3_readl(dwc->regs, DWC3_GRXFIFOSIZ(0));
if (dwc3_is_usb31(dwc))
size = DWC31_GRXFIFOSIZ_RXFDEP(size);
else
if (DWC3_IP_IS(DWC3))
size = DWC3_GRXFIFOSIZ_RXFDEP(size);
else
size = DWC31_GRXFIFOSIZ_RXFDEP(size);
/* FIFO depth is in MDWDITH bytes */
size *= mdwidth;
@@ -2531,10 +2656,8 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
req->request.actual = req->request.length - req->remaining;
if (!dwc3_gadget_ep_request_completed(req)) {
__dwc3_gadget_kick_transfer(dep);
if (!dwc3_gadget_ep_request_completed(req))
goto out;
}
dwc3_gadget_giveback(dep, req, status);
@@ -2558,41 +2681,53 @@ static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep,
}
}
static bool dwc3_gadget_ep_should_continue(struct dwc3_ep *dep)
{
struct dwc3_request *req;
if (!list_empty(&dep->pending_list))
return true;
/*
* We only need to check the first entry of the started list. We can
* assume the completed requests are removed from the started list.
*/
req = next_request(&dep->started_list);
if (!req)
return false;
return !dwc3_gadget_ep_request_completed(req);
}
static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event)
{
dep->frame_number = event->parameters;
}
static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event)
static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event, int status)
{
struct dwc3 *dwc = dep->dwc;
unsigned status = 0;
bool stop = false;
dwc3_gadget_endpoint_frame_from_event(dep, event);
if (event->status & DEPEVT_STATUS_BUSERR)
status = -ECONNRESET;
if (event->status & DEPEVT_STATUS_MISSED_ISOC) {
status = -EXDEV;
if (list_empty(&dep->started_list))
stop = true;
}
bool no_started_trb = true;
dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
if (stop)
dwc3_stop_active_transfer(dep, true, true);
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
goto out;
if (status == -EXDEV && list_empty(&dep->started_list))
dwc3_stop_active_transfer(dep, true, true);
else if (dwc3_gadget_ep_should_continue(dep))
if (__dwc3_gadget_kick_transfer(dep) == 0)
no_started_trb = false;
out:
/*
* WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
* See dwc3_gadget_linksts_change_interrupt() for 1st half.
*/
if (dwc->revision < DWC3_REVISION_183A) {
if (DWC3_VER_IS_PRIOR(DWC3, 183A)) {
u32 reg;
int i;
@@ -2603,7 +2738,7 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
continue;
if (!list_empty(&dep->started_list))
return;
return no_started_trb;
}
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
@@ -2612,15 +2747,124 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
dwc->u1u2 = 0;
}
return no_started_trb;
}
static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event)
{
int status = 0;
if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
dwc3_gadget_endpoint_frame_from_event(dep, event);
if (event->status & DEPEVT_STATUS_BUSERR)
status = -ECONNRESET;
if (event->status & DEPEVT_STATUS_MISSED_ISOC)
status = -EXDEV;
dwc3_gadget_endpoint_trbs_complete(dep, event, status);
}
static void dwc3_gadget_endpoint_transfer_complete(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event)
{
int status = 0;
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
if (event->status & DEPEVT_STATUS_BUSERR)
status = -ECONNRESET;
if (dwc3_gadget_endpoint_trbs_complete(dep, event, status))
dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE;
}
static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event)
{
dwc3_gadget_endpoint_frame_from_event(dep, event);
/*
* The XferNotReady event is generated only once before the endpoint
* starts. It will be generated again when END_TRANSFER command is
* issued. For some controller versions, the XferNotReady event may be
* generated while the END_TRANSFER command is still in process. Ignore
* it and wait for the next XferNotReady event after the command is
* completed.
*/
if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
return;
(void) __dwc3_gadget_start_isoc(dep);
}
static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
const struct dwc3_event_depevt *event)
{
struct dwc3 *dwc = dep->dwc;
if (event->status == DEPEVT_STREAMEVT_FOUND) {
dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
goto out;
}
/* Note: NoStream rejection event param value is 0 and not 0xFFFF */
switch (event->parameters) {
case DEPEVT_STREAM_PRIME:
/*
* If the host can properly transition the endpoint state from
* idle to prime after a NoStream rejection, there's no need to
* force restarting the endpoint to reinitiate the stream. To
* simplify the check, assume the host follows the USB spec if
* it primed the endpoint more than once.
*/
if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM) {
if (dep->flags & DWC3_EP_FIRST_STREAM_PRIMED)
dep->flags &= ~DWC3_EP_FORCE_RESTART_STREAM;
else
dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
}
break;
case DEPEVT_STREAM_NOSTREAM:
if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) ||
!(dep->flags & DWC3_EP_FORCE_RESTART_STREAM) ||
!(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE))
break;
/*
* If the host rejects a stream due to no active stream, by the
* USB and xHCI spec, the endpoint will be put back to idle
* state. When the host is ready (buffer added/updated), it will
* prime the endpoint to inform the usb device controller. This
* triggers the device controller to issue ERDY to restart the
* stream. However, some hosts don't follow this and keep the
* endpoint in the idle state. No prime will come despite host
* streams are updated, and the device controller will not be
* triggered to generate ERDY to move the next stream data. To
* workaround this and maintain compatibility with various
* hosts, force to reinitate the stream until the host is ready
* instead of waiting for the host to prime the endpoint.
*/
if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) {
unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME;
dwc3_send_gadget_generic_command(dwc, cmd, dep->number);
} else {
dep->flags |= DWC3_EP_DELAY_START;
dwc3_stop_active_transfer(dep, true, true);
return;
}
break;
}
out:
dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
}
static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
const struct dwc3_event_depevt *event)
{
@@ -2665,8 +2909,12 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
dep->flags &= ~DWC3_EP_DELAY_START;
}
break;
case DWC3_DEPEVT_STREAMEVT:
case DWC3_DEPEVT_XFERCOMPLETE:
dwc3_gadget_endpoint_transfer_complete(dep, event);
break;
case DWC3_DEPEVT_STREAMEVT:
dwc3_gadget_endpoint_stream_event(dep, event);
break;
case DWC3_DEPEVT_RXTXFIFOEVT:
break;
}
@@ -2758,6 +3006,14 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
WARN_ON_ONCE(ret);
dep->resource_index = 0;
/*
* The END_TRANSFER command will cause the controller to generate a
* NoStream Event, and it's not due to the host DP NoStream rejection.
* Ignore the next NoStream event.
*/
if (dep->stream_capable)
dep->flags |= DWC3_EP_IGNORE_NEXT_NOSTREAM;
if (!interrupt)
dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
else
@@ -2838,7 +3094,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
* STAR#9000466709: RTL: Device : Disconnect event not
* generated if setup packet pending in FIFO
*/
if (dwc->revision < DWC3_REVISION_188A) {
if (DWC3_VER_IS_PRIOR(DWC3, 188A)) {
if (dwc->setup_packet_pending)
dwc3_gadget_disconnect_interrupt(dwc);
}
@@ -2897,7 +3153,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
* STAR#9000483510: RTL: SS : USB3 reset event may
* not be generated always when the link enters poll
*/
if (dwc->revision < DWC3_REVISION_190A)
if (DWC3_VER_IS_PRIOR(DWC3, 190A))
dwc3_gadget_reset_interrupt(dwc);
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
@@ -2925,7 +3181,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
/* Enable USB2 LPM Capability */
if ((dwc->revision > DWC3_REVISION_194A) &&
if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A) &&
(speed != DWC3_DSTS_SUPERSPEED) &&
(speed != DWC3_DSTS_SUPERSPEED_PLUS)) {
reg = dwc3_readl(dwc->regs, DWC3_DCFG);
@@ -2944,11 +3200,10 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
* BESL value in the LPM token is less than or equal to LPM
* NYET threshold.
*/
WARN_ONCE(dwc->revision < DWC3_REVISION_240A
&& dwc->has_lpm_erratum,
WARN_ONCE(DWC3_VER_IS_PRIOR(DWC3, 240A) && dwc->has_lpm_erratum,
"LPM Erratum not available on dwc3 revisions < 2.40a\n");
if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A)
if (dwc->has_lpm_erratum && !DWC3_VER_IS_PRIOR(DWC3, 240A))
reg |= DWC3_DCTL_NYET_THRES(dwc->lpm_nyet_threshold);
dwc3_gadget_dctl_write_safe(dwc, reg);
@@ -3019,7 +3274,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
* operational mode
*/
pwropt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1);
if ((dwc->revision < DWC3_REVISION_250A) &&
if (DWC3_VER_IS_PRIOR(DWC3, 250A) &&
(pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
(next == DWC3_LINK_STATE_RESUME)) {
@@ -3045,7 +3300,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
* STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us
* core send LGO_Ux entering U0
*/
if (dwc->revision < DWC3_REVISION_183A) {
if (DWC3_VER_IS_PRIOR(DWC3, 183A)) {
if (next == DWC3_LINK_STATE_U0) {
u32 u1u2;
u32 reg;
@@ -3156,7 +3411,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
break;
case DWC3_DEVICE_EVENT_EOPF:
/* It changed to be suspend event for version 2.30a and above */
if (dwc->revision >= DWC3_REVISION_230A) {
if (!DWC3_VER_IS_PRIOR(DWC3, 230A)) {
/*
* Ignore suspend event until the gadget enters into
* USB_STATE_CONFIGURED state.
@@ -3401,7 +3656,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
* is less than super speed because we don't have means, yet, to tell
* composite.c that we are USB 2.0 + LPM ECN.
*/
if (dwc->revision < DWC3_REVISION_220A &&
if (DWC3_VER_IS_PRIOR(DWC3, 220A) &&
!dwc->dis_metastability_quirk)
dev_info(dwc->dev, "changing max_speed on rev %08x\n",
dwc->revision);

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* gadget.h - DesignWare USB3 DRD Gadget Header
*

View File

@@ -104,7 +104,7 @@ int dwc3_host_init(struct dwc3 *dwc)
*
* This following flag tells XHCI to do just that.
*/
if (dwc->revision <= DWC3_REVISION_300A)
if (DWC3_VER_IS_WITHIN(DWC3, ANY, 300A))
props[prop_idx++] = PROPERTY_ENTRY_BOOL("quirk-broken-port-ped");
if (prop_idx) {

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/**
* io.h - DesignWare USB3 DRD IO Header
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/**
* trace.h - DesignWare USB3 DRD Controller Trace Support
*

View File

@@ -18,7 +18,6 @@
#include <asm/fixmap.h>
#include <linux/bcd.h>
#include <linux/export.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kthread.h>

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* xhci-dbc.h - xHCI debug capability early driver
*

View File

@@ -96,40 +96,43 @@ function_descriptors(struct usb_function *f,
}
/**
* next_ep_desc() - advance to the next EP descriptor
* next_desc() - advance to the next desc_type descriptor
* @t: currect pointer within descriptor array
* @desc_type: descriptor type
*
* Return: next EP descriptor or NULL
* Return: next desc_type descriptor or NULL
*
* Iterate over @t until either EP descriptor found or
* Iterate over @t until either desc_type descriptor found or
* NULL (that indicates end of list) encountered
*/
static struct usb_descriptor_header**
next_ep_desc(struct usb_descriptor_header **t)
next_desc(struct usb_descriptor_header **t, u8 desc_type)
{
for (; *t; t++) {
if ((*t)->bDescriptorType == USB_DT_ENDPOINT)
if ((*t)->bDescriptorType == desc_type)
return t;
}
return NULL;
}
/*
* for_each_ep_desc()- iterate over endpoint descriptors in the
* descriptors list
* @start: pointer within descriptor array.
* @ep_desc: endpoint descriptor to use as the loop cursor
* for_each_desc() - iterate over desc_type descriptors in the
* descriptors list
* @start: pointer within descriptor array.
* @iter_desc: desc_type descriptor to use as the loop cursor
* @desc_type: wanted descriptr type
*/
#define for_each_ep_desc(start, ep_desc) \
for (ep_desc = next_ep_desc(start); \
ep_desc; ep_desc = next_ep_desc(ep_desc+1))
#define for_each_desc(start, iter_desc, desc_type) \
for (iter_desc = next_desc(start, desc_type); \
iter_desc; iter_desc = next_desc(iter_desc + 1, desc_type))
/**
* config_ep_by_speed() - configures the given endpoint
* config_ep_by_speed_and_alt() - configures the given endpoint
* according to gadget speed.
* @g: pointer to the gadget
* @f: usb function
* @_ep: the endpoint to configure
* @alt: alternate setting number
*
* Return: error code, 0 on success
*
@@ -142,11 +145,13 @@ next_ep_desc(struct usb_descriptor_header **t)
* Note: the supplied function should hold all the descriptors
* for supported speeds
*/
int config_ep_by_speed(struct usb_gadget *g,
struct usb_function *f,
struct usb_ep *_ep)
int config_ep_by_speed_and_alt(struct usb_gadget *g,
struct usb_function *f,
struct usb_ep *_ep,
u8 alt)
{
struct usb_endpoint_descriptor *chosen_desc = NULL;
struct usb_interface_descriptor *int_desc = NULL;
struct usb_descriptor_header **speed_desc = NULL;
struct usb_ss_ep_comp_descriptor *comp_desc = NULL;
@@ -182,8 +187,21 @@ int config_ep_by_speed(struct usb_gadget *g,
default:
speed_desc = f->fs_descriptors;
}
/* find correct alternate setting descriptor */
for_each_desc(speed_desc, d_spd, USB_DT_INTERFACE) {
int_desc = (struct usb_interface_descriptor *)*d_spd;
if (int_desc->bAlternateSetting == alt) {
speed_desc = d_spd;
goto intf_found;
}
}
return -EIO;
intf_found:
/* find descriptors */
for_each_ep_desc(speed_desc, d_spd) {
for_each_desc(speed_desc, d_spd, USB_DT_ENDPOINT) {
chosen_desc = (struct usb_endpoint_descriptor *)*d_spd;
if (chosen_desc->bEndpointAddress == _ep->address)
goto ep_found;
@@ -237,6 +255,32 @@ ep_found:
}
return 0;
}
EXPORT_SYMBOL_GPL(config_ep_by_speed_and_alt);
/**
* config_ep_by_speed() - configures the given endpoint
* according to gadget speed.
* @g: pointer to the gadget
* @f: usb function
* @_ep: the endpoint to configure
*
* Return: error code, 0 on success
*
* This function chooses the right descriptors for a given
* endpoint according to gadget speed and saves it in the
* endpoint desc field. If the endpoint already has a descriptor
* assigned to it - overwrites it with currently corresponding
* descriptor. The endpoint maxpacket field is updated according
* to the chosen descriptor.
* Note: the supplied function should hold all the descriptors
* for supported speeds
*/
int config_ep_by_speed(struct usb_gadget *g,
struct usb_function *f,
struct usb_ep *_ep)
{
return config_ep_by_speed_and_alt(g, f, _ep, 0);
}
EXPORT_SYMBOL_GPL(config_ep_by_speed);
/**

View File

@@ -13,8 +13,6 @@
int check_user_usb_string(const char *name,
struct usb_gadget_strings *stringtab_dev)
{
unsigned primary_lang;
unsigned sub_lang;
u16 num;
int ret;
@@ -22,17 +20,7 @@ int check_user_usb_string(const char *name,
if (ret)
return ret;
primary_lang = num & 0x3ff;
sub_lang = num >> 10;
/* simple sanity check for valid langid */
switch (primary_lang) {
case 0:
case 0x62 ... 0xfe:
case 0x100 ... 0x3ff:
return -EINVAL;
}
if (!sub_lang)
if (!usb_validate_langid(num))
return -EINVAL;
stringtab_dev->language = num;

View File

@@ -723,6 +723,20 @@ static void acm_free_func(struct usb_function *f)
kfree(acm);
}
static void acm_resume(struct usb_function *f)
{
struct f_acm *acm = func_to_acm(f);
gserial_resume(&acm->port);
}
static void acm_suspend(struct usb_function *f)
{
struct f_acm *acm = func_to_acm(f);
gserial_suspend(&acm->port);
}
static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
{
struct f_serial_opts *opts;
@@ -750,6 +764,8 @@ static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
acm->port_num = opts->port_num;
acm->port.func.unbind = acm_unbind;
acm->port.func.free_func = acm_free_func;
acm->port.func.resume = acm_resume;
acm->port.func.suspend = acm_suspend;
return &acm->port.func;
}

View File

@@ -291,8 +291,6 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
goto fail;
eem->port.out_ep = ep;
status = -ENOMEM;
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds

View File

@@ -2508,7 +2508,7 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
os_descs_count = get_unaligned_le32(data);
data += 4;
len -= 4;
};
}
/* Read descriptors */
raw_descs = data;

View File

@@ -348,6 +348,20 @@ static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
usb_free_all_descriptors(f);
}
static void gser_resume(struct usb_function *f)
{
struct f_gser *gser = func_to_gser(f);
gserial_resume(&gser->port);
}
static void gser_suspend(struct usb_function *f)
{
struct f_gser *gser = func_to_gser(f);
gserial_suspend(&gser->port);
}
static struct usb_function *gser_alloc(struct usb_function_instance *fi)
{
struct f_gser *gser;
@@ -369,6 +383,8 @@ static struct usb_function *gser_alloc(struct usb_function_instance *fi)
gser->port.func.set_alt = gser_set_alt;
gser->port.func.disable = gser_disable;
gser->port.func.free_func = gser_free;
gser->port.func.resume = gser_resume;
gser->port.func.suspend = gser_suspend;
return &gser->port.func;
}

View File

@@ -531,6 +531,7 @@ static int uasp_prepare_r_request(struct usbg_cmd *cmd)
stream->req_in->sg = se_cmd->t_data_sg;
}
stream->req_in->is_last = 1;
stream->req_in->complete = uasp_status_data_cmpl;
stream->req_in->length = se_cmd->data_length;
stream->req_in->context = cmd;
@@ -554,6 +555,7 @@ static void uasp_prepare_status(struct usbg_cmd *cmd)
*/
iu->len = cpu_to_be16(se_cmd->scsi_sense_length);
iu->status = se_cmd->scsi_status;
stream->req_status->is_last = 1;
stream->req_status->context = cmd;
stream->req_status->length = se_cmd->scsi_sense_length + 16;
stream->req_status->buf = iu;
@@ -991,6 +993,7 @@ static int usbg_prepare_w_request(struct usbg_cmd *cmd, struct usb_request *req)
req->sg = se_cmd->t_data_sg;
}
req->is_last = 1;
req->complete = usbg_data_write_cmpl;
req->length = se_cmd->data_length;
req->context = cmd;

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* f_uvc.h -- USB Video Class Gadget driver
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* RNDIS Definitions for Remote NDIS
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* u_audio.h -- interface to USB gadget "ALSA sound card" utilities
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_ecm.h
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_eem.h
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* u_ether.h -- interface to USB gadget "ethernet link" utilities
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_ether_configfs.h
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_fs.h
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_gether.h
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_hid.h
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_midi.h
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_ncm.h
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* u_phonet.h - interface to Phonet
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_printer.h
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_rndis.h
*

View File

@@ -120,6 +120,8 @@ struct gs_port {
wait_queue_head_t drain_wait; /* wait while writes drain */
bool write_busy;
wait_queue_head_t close_wait;
bool suspended; /* port suspended */
bool start_delayed; /* delay start when suspended */
/* REVISIT this state ... */
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
@@ -630,13 +632,19 @@ static int gs_open(struct tty_struct *tty, struct file *file)
/* if connected, start the I/O stream */
if (port->port_usb) {
struct gserial *gser = port->port_usb;
/* if port is suspended, wait resume to start I/0 stream */
if (!port->suspended) {
struct gserial *gser = port->port_usb;
pr_debug("gs_open: start ttyGS%d\n", port->port_num);
gs_start_io(port);
pr_debug("gs_open: start ttyGS%d\n", port->port_num);
gs_start_io(port);
if (gser->connect)
gser->connect(gser);
if (gser->connect)
gser->connect(gser);
} else {
pr_debug("delay start of ttyGS%d\n", port->port_num);
port->start_delayed = true;
}
}
pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file);
@@ -680,7 +688,7 @@ raced_with_open:
pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file);
gser = port->port_usb;
if (gser && gser->disconnect)
if (gser && !port->suspended && gser->disconnect)
gser->disconnect(gser);
/* wait for circular write buffer to drain, disconnect, or at
@@ -708,6 +716,7 @@ raced_with_open:
else
kfifo_reset(&port->port_write_buf);
port->start_delayed = false;
port->port.count = 0;
port->port.tty = NULL;
@@ -1403,6 +1412,38 @@ void gserial_disconnect(struct gserial *gser)
}
EXPORT_SYMBOL_GPL(gserial_disconnect);
void gserial_suspend(struct gserial *gser)
{
struct gs_port *port = gser->ioport;
unsigned long flags;
spin_lock_irqsave(&port->port_lock, flags);
port->suspended = true;
spin_unlock_irqrestore(&port->port_lock, flags);
}
EXPORT_SYMBOL_GPL(gserial_suspend);
void gserial_resume(struct gserial *gser)
{
struct gs_port *port = gser->ioport;
unsigned long flags;
spin_lock_irqsave(&port->port_lock, flags);
port->suspended = false;
if (!port->start_delayed) {
spin_unlock_irqrestore(&port->port_lock, flags);
return;
}
pr_debug("delayed start ttyGS%d\n", port->port_num);
gs_start_io(port);
if (gser->connect)
gser->connect(gser);
port->start_delayed = false;
spin_unlock_irqrestore(&port->port_lock, flags);
}
EXPORT_SYMBOL_GPL(gserial_resume);
static int userial_init(void)
{
unsigned i;

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* u_serial.h - interface to USB gadget "serial port"/TTY utilities
*
@@ -68,6 +68,8 @@ ssize_t gserial_get_console(unsigned char port_num, char *page);
/* connect/disconnect is handled by individual functions */
int gserial_connect(struct gserial *, u8 port_num);
void gserial_disconnect(struct gserial *);
void gserial_suspend(struct gserial *p);
void gserial_resume(struct gserial *p);
/* functions are bound to configurations by a config or gadget driver */
int gser_bind_config(struct usb_configuration *c, u8 port_num);

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_tcm.h
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_uac1.h - Utility definitions for UAC1 function
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* u_uac1.h -- interface to USB gadget "ALSA AUDIO" utilities
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_uac2.h
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* u_uvc.h
*

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0+
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* uvc_gadget.h -- USB Video Class Gadget driver
*
@@ -77,6 +77,8 @@ struct uvc_video {
struct uvc_device *uvc;
struct usb_ep *ep;
struct work_struct pump;
/* Frame parameters */
u8 bpp;
u32 fcc;

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* uvc_configfs.h
*

View File

@@ -169,7 +169,9 @@ uvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
if (ret < 0)
return ret;
return uvcg_video_pump(video);
schedule_work(&video->pump);
return ret;
}
static int

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* uvc_v4l2.h -- USB Video Class Gadget driver
*

View File

@@ -142,44 +142,12 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
return ret;
}
/*
* I somehow feel that synchronisation won't be easy to achieve here. We have
* three events that control USB requests submission:
*
* - USB request completion: the completion handler will resubmit the request
* if a video buffer is available.
*
* - USB interface setting selection: in response to a SET_INTERFACE request,
* the handler will start streaming if a video buffer is available and if
* video is not currently streaming.
*
* - V4L2 buffer queueing: the driver will start streaming if video is not
* currently streaming.
*
* Race conditions between those 3 events might lead to deadlocks or other
* nasty side effects.
*
* The "video currently streaming" condition can't be detected by the irqqueue
* being empty, as a request can still be in flight. A separate "queue paused"
* flag is thus needed.
*
* The paused flag will be set when we try to retrieve the irqqueue head if the
* queue is empty, and cleared when we queue a buffer.
*
* The USB request completion handler will get the buffer at the irqqueue head
* under protection of the queue spinlock. If the queue is empty, the streaming
* paused flag will be set. Right after releasing the spinlock a userspace
* application can queue a buffer. The flag will then cleared, and the ioctl
* handler will restart the video stream.
*/
static void
uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
{
struct uvc_video *video = req->context;
struct uvc_video_queue *queue = &video->queue;
struct uvc_buffer *buf;
unsigned long flags;
int ret;
switch (req->status) {
case 0:
@@ -188,39 +156,20 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
case -ESHUTDOWN: /* disconnect from host. */
uvcg_dbg(&video->uvc->func, "VS request cancelled.\n");
uvcg_queue_cancel(queue, 1);
goto requeue;
break;
default:
uvcg_info(&video->uvc->func,
"VS request completed with status %d.\n",
req->status);
uvcg_queue_cancel(queue, 0);
goto requeue;
}
spin_lock_irqsave(&video->queue.irqlock, flags);
buf = uvcg_queue_head(&video->queue);
if (buf == NULL) {
spin_unlock_irqrestore(&video->queue.irqlock, flags);
goto requeue;
}
video->encode(req, video, buf);
ret = uvcg_video_ep_queue(video, req);
spin_unlock_irqrestore(&video->queue.irqlock, flags);
if (ret < 0) {
uvcg_queue_cancel(queue, 0);
goto requeue;
}
return;
requeue:
spin_lock_irqsave(&video->req_lock, flags);
list_add_tail(&req->list, &video->req_free);
spin_unlock_irqrestore(&video->req_lock, flags);
schedule_work(&video->pump);
}
static int
@@ -294,18 +243,15 @@ error:
* This function fills the available USB requests (listed in req_free) with
* video data from the queued buffers.
*/
int uvcg_video_pump(struct uvc_video *video)
static void uvcg_video_pump(struct work_struct *work)
{
struct uvc_video *video = container_of(work, struct uvc_video, pump);
struct uvc_video_queue *queue = &video->queue;
struct usb_request *req;
struct uvc_buffer *buf;
unsigned long flags;
int ret;
/* FIXME TODO Race between uvcg_video_pump and requests completion
* handler ???
*/
while (1) {
/* Retrieve the first available USB request, protected by the
* request lock.
@@ -313,7 +259,7 @@ int uvcg_video_pump(struct uvc_video *video)
spin_lock_irqsave(&video->req_lock, flags);
if (list_empty(&video->req_free)) {
spin_unlock_irqrestore(&video->req_lock, flags);
return 0;
return;
}
req = list_first_entry(&video->req_free, struct usb_request,
list);
@@ -345,7 +291,7 @@ int uvcg_video_pump(struct uvc_video *video)
spin_lock_irqsave(&video->req_lock, flags);
list_add_tail(&req->list, &video->req_free);
spin_unlock_irqrestore(&video->req_lock, flags);
return 0;
return;
}
/*
@@ -363,6 +309,9 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
}
if (!enable) {
cancel_work_sync(&video->pump);
uvcg_queue_cancel(&video->queue, 0);
for (i = 0; i < UVC_NUM_REQUESTS; ++i)
if (video->req[i])
usb_ep_dequeue(video->ep, video->req[i]);
@@ -384,7 +333,9 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
} else
video->encode = uvc_video_encode_isoc;
return uvcg_video_pump(video);
schedule_work(&video->pump);
return ret;
}
/*
@@ -394,6 +345,7 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
{
INIT_LIST_HEAD(&video->req_free);
spin_lock_init(&video->req_lock);
INIT_WORK(&video->pump, uvcg_video_pump);
video->uvc = uvc;
video->fcc = V4L2_PIX_FMT_YUYV;

View File

@@ -1,4 +1,4 @@
// SPDX-License-Identifier: GPL-2.0
/* SPDX-License-Identifier: GPL-2.0 */
/*
* uvc_video.h -- USB Video Class Gadget driver
*
@@ -14,8 +14,6 @@
struct uvc_video;
int uvcg_video_pump(struct uvc_video *video);
int uvcg_video_enable(struct uvc_video *video, int enable);
int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc);

View File

@@ -229,18 +229,8 @@ static struct usb_composite_driver msg_driver = {
.unbind = msg_unbind,
};
module_usb_composite_driver(msg_driver);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Michal Nazarewicz");
MODULE_LICENSE("GPL");
static int __init msg_init(void)
{
return usb_composite_probe(&msg_driver);
}
module_init(msg_init);
static void __exit msg_cleanup(void)
{
usb_composite_unregister(&msg_driver);
}
module_exit(msg_cleanup);

View File

@@ -134,11 +134,15 @@ static irqreturn_t ast_vhub_irq(int irq, void *data)
}
/* Handle device interrupts */
for (i = 0; i < vhub->max_ports; i++) {
u32 dev_mask = VHUB_IRQ_DEVICE1 << i;
if (istat & vhub->port_irq_mask) {
unsigned long bitmap = istat;
int offset = VHUB_IRQ_DEV1_BIT;
int size = VHUB_IRQ_DEV1_BIT + vhub->max_ports;
if (istat & dev_mask)
for_each_set_bit_from(offset, &bitmap, size) {
i = offset - VHUB_IRQ_DEV1_BIT;
ast_vhub_dev_irq(&vhub->ports[i].dev);
}
}
/* Handle top-level vHub EP0 interrupts */
@@ -332,6 +336,8 @@ static int ast_vhub_probe(struct platform_device *pdev)
spin_lock_init(&vhub->lock);
vhub->pdev = pdev;
vhub->port_irq_mask = GENMASK(VHUB_IRQ_DEV1_BIT + vhub->max_ports - 1,
VHUB_IRQ_DEV1_BIT);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
vhub->regs = devm_ioremap_resource(&pdev->dev, res);
@@ -402,7 +408,9 @@ static int ast_vhub_probe(struct platform_device *pdev)
goto err;
/* Init hub emulation */
ast_vhub_init_hub(vhub);
rc = ast_vhub_init_hub(vhub);
if (rc)
goto err;
/* Initialize HW */
ast_vhub_init_hw(vhub);

View File

@@ -50,6 +50,7 @@
#define KERNEL_VER bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff))
enum {
AST_VHUB_STR_INDEX_MAX = 4,
AST_VHUB_STR_MANUF = 3,
AST_VHUB_STR_PRODUCT = 2,
AST_VHUB_STR_SERIAL = 1,
@@ -72,13 +73,6 @@ static const struct usb_device_descriptor ast_vhub_dev_desc = {
.bNumConfigurations = 1,
};
/* Patches to the above when forcing USB1 mode */
static void ast_vhub_patch_dev_desc_usb1(struct usb_device_descriptor *desc)
{
desc->bcdUSB = cpu_to_le16(0x0100);
desc->bDeviceProtocol = 0;
}
/*
* Configuration descriptor: same comments as above
* regarding handling USB1 mode.
@@ -302,31 +296,81 @@ static int ast_vhub_rep_desc(struct ast_vhub_ep *ep,
if (len > dsize)
len = dsize;
/* Patch it if forcing USB1 */
if (desc_type == USB_DT_DEVICE && ep->vhub->force_usb1)
ast_vhub_patch_dev_desc_usb1(ep->buf);
/* Shoot it from the EP buffer */
return ast_vhub_reply(ep, NULL, len);
}
static struct usb_gadget_strings*
ast_vhub_str_of_container(struct usb_gadget_string_container *container)
{
return (struct usb_gadget_strings *)container->stash;
}
static int ast_vhub_collect_languages(struct ast_vhub *vhub, void *buf,
size_t size)
{
int rc, hdr_len, nlangs, max_langs;
struct usb_gadget_strings *lang_str;
struct usb_gadget_string_container *container;
struct usb_string_descriptor *sdesc = buf;
nlangs = 0;
hdr_len = sizeof(struct usb_descriptor_header);
max_langs = (size - hdr_len) / sizeof(sdesc->wData[0]);
list_for_each_entry(container, &vhub->vhub_str_desc, list) {
if (nlangs >= max_langs)
break;
lang_str = ast_vhub_str_of_container(container);
sdesc->wData[nlangs++] = cpu_to_le16(lang_str->language);
}
rc = hdr_len + nlangs * sizeof(sdesc->wData[0]);
sdesc->bLength = rc;
sdesc->bDescriptorType = USB_DT_STRING;
return rc;
}
static struct usb_gadget_strings *ast_vhub_lookup_string(struct ast_vhub *vhub,
u16 lang_id)
{
struct usb_gadget_strings *lang_str;
struct usb_gadget_string_container *container;
list_for_each_entry(container, &vhub->vhub_str_desc, list) {
lang_str = ast_vhub_str_of_container(container);
if (lang_str->language == lang_id)
return lang_str;
}
return NULL;
}
static int ast_vhub_rep_string(struct ast_vhub_ep *ep,
u8 string_id, u16 lang_id,
u16 len)
{
int rc = usb_gadget_get_string(&ep->vhub->vhub_str_desc,
string_id, ep->buf);
int rc;
u8 buf[256];
struct ast_vhub *vhub = ep->vhub;
struct usb_gadget_strings *lang_str;
/*
* This should never happen unless we put too big strings in
* the array above
*/
BUG_ON(rc >= AST_VHUB_EP0_MAX_PACKET);
if (string_id == 0) {
rc = ast_vhub_collect_languages(vhub, buf, sizeof(buf));
} else {
lang_str = ast_vhub_lookup_string(vhub, lang_id);
if (!lang_str)
return std_req_stall;
if (rc < 0)
rc = usb_gadget_get_string(lang_str, string_id, buf);
}
if (rc < 0 || rc >= AST_VHUB_EP0_MAX_PACKET)
return std_req_stall;
/* Shoot it from the EP buffer */
memcpy(ep->buf, buf, rc);
return ast_vhub_reply(ep, NULL, min_t(u16, rc, len));
}
@@ -832,11 +876,148 @@ void ast_vhub_hub_reset(struct ast_vhub *vhub)
writel(0, vhub->regs + AST_VHUB_EP1_STS_CHG);
}
static void ast_vhub_init_desc(struct ast_vhub *vhub)
static void ast_vhub_of_parse_dev_desc(struct ast_vhub *vhub,
const struct device_node *vhub_np)
{
u16 id;
u32 data;
if (!of_property_read_u32(vhub_np, "vhub-vendor-id", &data)) {
id = (u16)data;
vhub->vhub_dev_desc.idVendor = cpu_to_le16(id);
}
if (!of_property_read_u32(vhub_np, "vhub-product-id", &data)) {
id = (u16)data;
vhub->vhub_dev_desc.idProduct = cpu_to_le16(id);
}
if (!of_property_read_u32(vhub_np, "vhub-device-revision", &data)) {
id = (u16)data;
vhub->vhub_dev_desc.bcdDevice = cpu_to_le16(id);
}
}
static void ast_vhub_fixup_usb1_dev_desc(struct ast_vhub *vhub)
{
vhub->vhub_dev_desc.bcdUSB = cpu_to_le16(0x0100);
vhub->vhub_dev_desc.bDeviceProtocol = 0;
}
static struct usb_gadget_string_container*
ast_vhub_str_container_alloc(struct ast_vhub *vhub)
{
unsigned int size;
struct usb_string *str_array;
struct usb_gadget_strings *lang_str;
struct usb_gadget_string_container *container;
size = sizeof(*container);
size += sizeof(struct usb_gadget_strings);
size += sizeof(struct usb_string) * AST_VHUB_STR_INDEX_MAX;
container = devm_kzalloc(&vhub->pdev->dev, size, GFP_KERNEL);
if (!container)
return ERR_PTR(-ENOMEM);
lang_str = ast_vhub_str_of_container(container);
str_array = (struct usb_string *)(lang_str + 1);
lang_str->strings = str_array;
return container;
}
static void ast_vhub_str_deep_copy(struct usb_gadget_strings *dest,
const struct usb_gadget_strings *src)
{
struct usb_string *src_array = src->strings;
struct usb_string *dest_array = dest->strings;
dest->language = src->language;
if (src_array && dest_array) {
do {
*dest_array = *src_array;
dest_array++;
src_array++;
} while (src_array->s);
}
}
static int ast_vhub_str_alloc_add(struct ast_vhub *vhub,
const struct usb_gadget_strings *src_str)
{
struct usb_gadget_strings *dest_str;
struct usb_gadget_string_container *container;
container = ast_vhub_str_container_alloc(vhub);
if (IS_ERR(container))
return PTR_ERR(container);
dest_str = ast_vhub_str_of_container(container);
ast_vhub_str_deep_copy(dest_str, src_str);
list_add_tail(&container->list, &vhub->vhub_str_desc);
return 0;
}
static const struct {
const char *name;
u8 id;
} str_id_map[] = {
{"manufacturer", AST_VHUB_STR_MANUF},
{"product", AST_VHUB_STR_PRODUCT},
{"serial-number", AST_VHUB_STR_SERIAL},
{},
};
static int ast_vhub_of_parse_str_desc(struct ast_vhub *vhub,
const struct device_node *desc_np)
{
u32 langid;
int ret = 0;
int i, offset;
const char *str;
struct device_node *child;
struct usb_string str_array[AST_VHUB_STR_INDEX_MAX];
struct usb_gadget_strings lang_str = {
.strings = (struct usb_string *)str_array,
};
for_each_child_of_node(desc_np, child) {
if (of_property_read_u32(child, "reg", &langid))
continue; /* no language identifier specified */
if (!usb_validate_langid(langid))
continue; /* invalid language identifier */
lang_str.language = langid;
for (i = offset = 0; str_id_map[i].name; i++) {
str = of_get_property(child, str_id_map[i].name, NULL);
if (str) {
str_array[offset].s = str;
str_array[offset].id = str_id_map[i].id;
offset++;
}
}
str_array[offset].id = 0;
str_array[offset].s = NULL;
ret = ast_vhub_str_alloc_add(vhub, &lang_str);
if (ret)
break;
}
return ret;
}
static int ast_vhub_init_desc(struct ast_vhub *vhub)
{
int ret;
struct device_node *desc_np;
const struct device_node *vhub_np = vhub->pdev->dev.of_node;
/* Initialize vhub Device Descriptor. */
memcpy(&vhub->vhub_dev_desc, &ast_vhub_dev_desc,
sizeof(vhub->vhub_dev_desc));
ast_vhub_of_parse_dev_desc(vhub, vhub_np);
if (vhub->force_usb1)
ast_vhub_fixup_usb1_dev_desc(vhub);
/* Initialize vhub Configuration Descriptor. */
memcpy(&vhub->vhub_conf_desc, &ast_vhub_conf_desc,
@@ -848,15 +1029,20 @@ static void ast_vhub_init_desc(struct ast_vhub *vhub)
vhub->vhub_hub_desc.bNbrPorts = vhub->max_ports;
/* Initialize vhub String Descriptors. */
memcpy(&vhub->vhub_str_desc, &ast_vhub_strings,
sizeof(vhub->vhub_str_desc));
INIT_LIST_HEAD(&vhub->vhub_str_desc);
desc_np = of_get_child_by_name(vhub_np, "vhub-strings");
if (desc_np)
ret = ast_vhub_of_parse_str_desc(vhub, desc_np);
else
ret = ast_vhub_str_alloc_add(vhub, &ast_vhub_strings);
return ret;
}
void ast_vhub_init_hub(struct ast_vhub *vhub)
int ast_vhub_init_hub(struct ast_vhub *vhub)
{
vhub->speed = USB_SPEED_UNKNOWN;
INIT_WORK(&vhub->wake_work, ast_vhub_wake_work);
ast_vhub_init_desc(vhub);
return ast_vhub_init_desc(vhub);
}

View File

@@ -51,14 +51,11 @@
#define VHUB_CTRL_UPSTREAM_CONNECT (1 << 0)
/* IER & ISR */
#define VHUB_IRQ_DEV1_BIT 9
#define VHUB_IRQ_USB_CMD_DEADLOCK (1 << 18)
#define VHUB_IRQ_EP_POOL_NAK (1 << 17)
#define VHUB_IRQ_EP_POOL_ACK_STALL (1 << 16)
#define VHUB_IRQ_DEVICE5 (1 << 13)
#define VHUB_IRQ_DEVICE4 (1 << 12)
#define VHUB_IRQ_DEVICE3 (1 << 11)
#define VHUB_IRQ_DEVICE2 (1 << 10)
#define VHUB_IRQ_DEVICE1 (1 << 9)
#define VHUB_IRQ_DEVICE1 (1 << (VHUB_IRQ_DEV1_BIT))
#define VHUB_IRQ_BUS_RESUME (1 << 8)
#define VHUB_IRQ_BUS_SUSPEND (1 << 7)
#define VHUB_IRQ_BUS_RESET (1 << 6)
@@ -402,6 +399,7 @@ struct ast_vhub {
/* Per-port info */
struct ast_vhub_port *ports;
u32 max_ports;
u32 port_irq_mask;
/* Generic EP data structures */
struct ast_vhub_ep *epns;
@@ -423,7 +421,7 @@ struct ast_vhub {
struct usb_device_descriptor vhub_dev_desc;
struct ast_vhub_full_cdesc vhub_conf_desc;
struct usb_hub_descriptor vhub_hub_desc;
struct usb_gadget_strings vhub_str_desc;
struct list_head vhub_str_desc;
};
/* Standard request handlers result codes */
@@ -533,7 +531,7 @@ int __ast_vhub_simple_reply(struct ast_vhub_ep *ep, int len, ...);
__VA_ARGS__)
/* hub.c */
void ast_vhub_init_hub(struct ast_vhub *vhub);
int ast_vhub_init_hub(struct ast_vhub *vhub);
enum std_req_rc ast_vhub_std_hub_request(struct ast_vhub_ep *ep,
struct usb_ctrlrequest *crq);
enum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep,

View File

@@ -2043,10 +2043,56 @@ static const struct usba_udc_errata at91sam9g45_errata = {
.pulse_bias = at91sam9g45_pulse_bias,
};
static const struct usba_ep_config ep_config_sam9[] __initconst = {
{ .nr_banks = 1 }, /* ep 0 */
{ .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 1 */
{ .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 2 */
{ .nr_banks = 3, .can_dma = 1 }, /* ep 3 */
{ .nr_banks = 3, .can_dma = 1 }, /* ep 4 */
{ .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 5 */
{ .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 6 */
};
static const struct usba_ep_config ep_config_sama5[] __initconst = {
{ .nr_banks = 1 }, /* ep 0 */
{ .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 1 */
{ .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 2 */
{ .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 3 */
{ .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 4 */
{ .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 5 */
{ .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 6 */
{ .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 7 */
{ .nr_banks = 2, .can_isoc = 1 }, /* ep 8 */
{ .nr_banks = 2, .can_isoc = 1 }, /* ep 9 */
{ .nr_banks = 2, .can_isoc = 1 }, /* ep 10 */
{ .nr_banks = 2, .can_isoc = 1 }, /* ep 11 */
{ .nr_banks = 2, .can_isoc = 1 }, /* ep 12 */
{ .nr_banks = 2, .can_isoc = 1 }, /* ep 13 */
{ .nr_banks = 2, .can_isoc = 1 }, /* ep 14 */
{ .nr_banks = 2, .can_isoc = 1 }, /* ep 15 */
};
static const struct usba_udc_config udc_at91sam9rl_cfg = {
.errata = &at91sam9rl_errata,
.config = ep_config_sam9,
.num_ep = ARRAY_SIZE(ep_config_sam9),
};
static const struct usba_udc_config udc_at91sam9g45_cfg = {
.errata = &at91sam9g45_errata,
.config = ep_config_sam9,
.num_ep = ARRAY_SIZE(ep_config_sam9),
};
static const struct usba_udc_config udc_sama5d3_cfg = {
.config = ep_config_sama5,
.num_ep = ARRAY_SIZE(ep_config_sama5),
};
static const struct of_device_id atmel_udc_dt_ids[] = {
{ .compatible = "atmel,at91sam9rl-udc", .data = &at91sam9rl_errata },
{ .compatible = "atmel,at91sam9g45-udc", .data = &at91sam9g45_errata },
{ .compatible = "atmel,sama5d3-udc" },
{ .compatible = "atmel,at91sam9rl-udc", .data = &udc_at91sam9rl_cfg },
{ .compatible = "atmel,at91sam9g45-udc", .data = &udc_at91sam9g45_cfg },
{ .compatible = "atmel,sama5d3-udc", .data = &udc_sama5d3_cfg },
{ /* sentinel */ }
};
@@ -2055,18 +2101,19 @@ MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids);
static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
struct usba_udc *udc)
{
u32 val;
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match;
struct device_node *pp;
int i, ret;
struct usba_ep *eps, *ep;
const struct usba_udc_config *udc_config;
match = of_match_node(atmel_udc_dt_ids, np);
if (!match)
return ERR_PTR(-EINVAL);
udc->errata = match->data;
udc_config = match->data;
udc->errata = udc_config->errata;
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9g45-pmc");
if (IS_ERR(udc->pmc))
udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9rl-pmc");
@@ -2082,8 +2129,7 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
if (fifo_mode == 0) {
pp = NULL;
while ((pp = of_get_next_child(np, pp)))
udc->num_ep++;
udc->num_ep = udc_config->num_ep;
udc->configured_ep = 1;
} else {
udc->num_ep = usba_config_fifo_table(udc);
@@ -2100,52 +2146,38 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
pp = NULL;
i = 0;
while ((pp = of_get_next_child(np, pp)) && i < udc->num_ep) {
while (i < udc->num_ep) {
const struct usba_ep_config *ep_cfg = &udc_config->config[i];
ep = &eps[i];
ret = of_property_read_u32(pp, "reg", &val);
if (ret) {
dev_err(&pdev->dev, "of_probe: reg error(%d)\n", ret);
goto err;
}
ep->index = fifo_mode ? udc->fifo_cfg[i].hw_ep_num : val;
ep->index = fifo_mode ? udc->fifo_cfg[i].hw_ep_num : i;
/* Only the first EP is 64 bytes */
if (ep->index == 0)
ep->fifo_size = 64;
else
ep->fifo_size = 1024;
ret = of_property_read_u32(pp, "atmel,fifo-size", &val);
if (ret) {
dev_err(&pdev->dev, "of_probe: fifo-size error(%d)\n", ret);
goto err;
}
if (fifo_mode) {
if (val < udc->fifo_cfg[i].fifo_size) {
if (ep->fifo_size < udc->fifo_cfg[i].fifo_size)
dev_warn(&pdev->dev,
"Using max fifo-size value from DT\n");
ep->fifo_size = val;
} else {
"Using default max fifo-size value\n");
else
ep->fifo_size = udc->fifo_cfg[i].fifo_size;
}
} else {
ep->fifo_size = val;
}
ret = of_property_read_u32(pp, "atmel,nb-banks", &val);
if (ret) {
dev_err(&pdev->dev, "of_probe: nb-banks error(%d)\n", ret);
goto err;
}
ep->nr_banks = ep_cfg->nr_banks;
if (fifo_mode) {
if (val < udc->fifo_cfg[i].nr_banks) {
if (ep->nr_banks < udc->fifo_cfg[i].nr_banks)
dev_warn(&pdev->dev,
"Using max nb-banks value from DT\n");
ep->nr_banks = val;
} else {
"Using default max nb-banks value\n");
else
ep->nr_banks = udc->fifo_cfg[i].nr_banks;
}
} else {
ep->nr_banks = val;
}
ep->can_dma = of_property_read_bool(pp, "atmel,can-dma");
ep->can_isoc = of_property_read_bool(pp, "atmel,can-isoc");
ep->can_dma = ep_cfg->can_dma;
ep->can_isoc = ep_cfg->can_isoc;
sprintf(ep->name, "ep%d", ep->index);
ep->ep.name = ep->name;

View File

@@ -290,6 +290,12 @@ struct usba_ep {
#endif
};
struct usba_ep_config {
u8 nr_banks;
unsigned int can_dma:1;
unsigned int can_isoc:1;
};
struct usba_request {
struct usb_request req;
struct list_head queue;
@@ -307,6 +313,12 @@ struct usba_udc_errata {
void (*pulse_bias)(struct usba_udc *udc);
};
struct usba_udc_config {
const struct usba_udc_errata *errata;
const struct usba_ep_config *config;
const int num_ep;
};
struct usba_udc {
/* Protect hw registers from concurrent modifications */
spinlock_t lock;

View File

@@ -1297,6 +1297,8 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
usb_gadget_disconnect(udc->gadget);
if (udc->gadget->irq)
synchronize_irq(udc->gadget->irq);
udc->driver->unbind(udc->gadget);
usb_gadget_udc_stop(udc);

View File

@@ -187,31 +187,31 @@ static const struct {
USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
/* and now some generic EPs so we have enough in multi config */
EP_INFO("ep3out",
EP_INFO("ep-aout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
EP_INFO("ep4in",
EP_INFO("ep-bin",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
EP_INFO("ep5out",
EP_INFO("ep-cout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
EP_INFO("ep6out",
EP_INFO("ep-dout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
EP_INFO("ep7in",
EP_INFO("ep-ein",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
EP_INFO("ep8out",
EP_INFO("ep-fout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
EP_INFO("ep9in",
EP_INFO("ep-gin",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
EP_INFO("ep10out",
EP_INFO("ep-hout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
EP_INFO("ep11out",
EP_INFO("ep-iout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
EP_INFO("ep12in",
EP_INFO("ep-jin",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
EP_INFO("ep13out",
EP_INFO("ep-kout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
EP_INFO("ep14in",
EP_INFO("ep-lin",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
EP_INFO("ep15out",
EP_INFO("ep-mout",
USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
#undef EP_INFO
@@ -427,6 +427,7 @@ static void set_link_state_by_speed(struct dummy_hcd *dum_hcd)
/* caller must hold lock */
static void set_link_state(struct dummy_hcd *dum_hcd)
__must_hold(&dum->lock)
{
struct dummy *dum = dum_hcd->dum;
unsigned int power_bit;

View File

@@ -2440,8 +2440,8 @@ static int fsl_udc_probe(struct platform_device *pdev)
udc_controller->max_ep = (dccparams & DCCPARAMS_DEN_MASK) * 2;
udc_controller->irq = platform_get_irq(pdev, 0);
if (!udc_controller->irq) {
ret = -ENODEV;
if (udc_controller->irq <= 0) {
ret = udc_controller->irq ? : -ENODEV;
goto err_iounmap;
}

View File

@@ -48,7 +48,6 @@
#define DRIVER_DESC "Aeroflex Gaisler GRUSBDC USB Peripheral Controller"
static const char driver_name[] = DRIVER_NAME;
static const char driver_desc[] = DRIVER_DESC;
#define gr_read32(x) (ioread32be((x)))
#define gr_write32(x, v) (iowrite32be((v), (x)))

View File

@@ -1614,17 +1614,17 @@ static int lpc32xx_ep_enable(struct usb_ep *_ep,
const struct usb_endpoint_descriptor *desc)
{
struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep);
struct lpc32xx_udc *udc = ep->udc;
struct lpc32xx_udc *udc;
u16 maxpacket;
u32 tmp;
unsigned long flags;
/* Verify EP data */
if ((!_ep) || (!ep) || (!desc) ||
(desc->bDescriptorType != USB_DT_ENDPOINT)) {
dev_dbg(udc->dev, "bad ep or descriptor\n");
(desc->bDescriptorType != USB_DT_ENDPOINT))
return -EINVAL;
}
udc = ep->udc;
maxpacket = usb_endpoint_maxp(desc);
if ((maxpacket == 0) || (maxpacket > ep->maxpacket)) {
dev_dbg(udc->dev, "bad ep descriptor's packet size\n");
@@ -1872,7 +1872,7 @@ static int lpc32xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
static int lpc32xx_ep_set_halt(struct usb_ep *_ep, int value)
{
struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep);
struct lpc32xx_udc *udc = ep->udc;
struct lpc32xx_udc *udc;
unsigned long flags;
if ((!ep) || (ep->hwep_num <= 1))
@@ -1882,6 +1882,7 @@ static int lpc32xx_ep_set_halt(struct usb_ep *_ep, int value)
if (ep->is_in)
return -EAGAIN;
udc = ep->udc;
spin_lock_irqsave(&udc->lock, flags);
if (value == 1) {

View File

@@ -1667,7 +1667,7 @@ static int m66592_probe(struct platform_device *pdev)
err_add_udc:
m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
m66592->ep0_req = NULL;
clean_up3:
if (m66592->pdata->on_chip) {
clk_disable(m66592->clk);

View File

@@ -901,7 +901,7 @@ loop:
}
set_current_state(TASK_RUNNING);
dev_info(udc->dev, "SPI thread exiting");
dev_info(udc->dev, "SPI thread exiting\n");
return 0;
}

View File

@@ -1548,7 +1548,7 @@ static void mv_u3d_handle_setup_packet(struct mv_u3d *u3d, u8 ep_num,
delegate = true;
/* delegate USB standard requests to the gadget driver */
if (delegate == true) {
if (delegate) {
/* USB requests handled by gadget */
if (setup->wLength) {
/* DATA phase from gadget, STATUS phase from u3d */

View File

@@ -54,7 +54,7 @@ static const char * const ep_name[] = {
*
* If use_dma is disabled, pio will be used instead.
*/
static bool use_dma = 0;
static bool use_dma = false;
module_param(use_dma, bool, 0644);
/*

View File

@@ -2576,7 +2576,7 @@ omap_ep_setup(char *name, u8 addr, u8 type,
case USB_ENDPOINT_XFER_INT:
ep->ep.caps.type_int = true;
break;
};
}
if (addr & USB_DIR_IN)
ep->ep.caps.dir_in = true;

View File

@@ -251,10 +251,6 @@ static void s3c2410_udc_done(struct s3c2410_ep *ep,
static void s3c2410_udc_nuke(struct s3c2410_udc *udc,
struct s3c2410_ep *ep, int status)
{
/* Sanity check */
if (&ep->queue == NULL)
return;
while (!list_empty(&ep->queue)) {
struct s3c2410_request *req;
req = list_entry(ep->queue.next, struct s3c2410_request,

Some files were not shown because too many files have changed in this diff Show More