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:
@@ -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;
|
||||
|
@@ -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)) {
|
||||
|
@@ -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);
|
||||
|
@@ -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));
|
||||
}
|
||||
|
@@ -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));
|
||||
|
@@ -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,
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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 &&
|
||||
|
@@ -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 */
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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");
|
@@ -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) {
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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 */ }
|
||||
};
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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 "
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* usb hub driver head file
|
||||
*
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* drivers/usb/core/otg_whitelist.h
|
||||
*
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Released under the GPLv2 only.
|
||||
*/
|
||||
|
@@ -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 */
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* debug.h - Designware USB2 DRD controller debug header
|
||||
*
|
||||
|
@@ -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
|
||||
*
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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.
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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:
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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, ®);
|
||||
regmap_read(priv->usb_glue_regmap, USB_R5, ®);
|
||||
|
||||
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,
|
||||
|
@@ -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 */ }
|
||||
};
|
||||
|
@@ -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, ¶ms);
|
||||
}
|
||||
|
||||
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, ¶ms);
|
||||
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, ¶ms);
|
||||
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(¶ms, 0, sizeof(params));
|
||||
|
||||
ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms);
|
||||
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);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* gadget.h - DesignWare USB3 DRD Gadget Header
|
||||
*
|
||||
|
@@ -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) {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/**
|
||||
* io.h - DesignWare USB3 DRD IO Header
|
||||
*
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/**
|
||||
* trace.h - DesignWare USB3 DRD Controller Trace Support
|
||||
*
|
||||
|
@@ -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>
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* xhci-dbc.h - xHCI debug capability early driver
|
||||
*
|
||||
|
@@ -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);
|
||||
|
||||
/**
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* f_uvc.h -- USB Video Class Gadget driver
|
||||
*
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* RNDIS Definitions for Remote NDIS
|
||||
*
|
||||
|
@@ -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
|
||||
*
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_ecm.h
|
||||
*
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_eem.h
|
||||
*
|
||||
|
@@ -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
|
||||
*
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_ether_configfs.h
|
||||
*
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_fs.h
|
||||
*
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_gether.h
|
||||
*
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_hid.h
|
||||
*
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_midi.h
|
||||
*
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_ncm.h
|
||||
*
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* u_phonet.h - interface to Phonet
|
||||
*
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_printer.h
|
||||
*
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_rndis.h
|
||||
*
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_tcm.h
|
||||
*
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_uac1.h - Utility definitions for UAC1 function
|
||||
*
|
||||
|
@@ -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
|
||||
*
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_uac2.h
|
||||
*
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* u_uvc.h
|
||||
*
|
||||
|
@@ -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;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* uvc_configfs.h
|
||||
*
|
||||
|
@@ -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
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* uvc_v4l2.h -- USB Video Class Gadget driver
|
||||
*
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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,
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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)))
|
||||
|
@@ -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) {
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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 */
|
||||
|
@@ -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);
|
||||
|
||||
/*
|
||||
|
@@ -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;
|
||||
|
@@ -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
Reference in New Issue
Block a user