summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-07-19 15:37:48 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2024-07-19 15:37:48 -0700
commit04d17331ca33744e1426fdeee7ba5e975c4b2239 (patch)
tree970a241cfd378f097c67d29bdaaaa6bf6e6c3413 /drivers/usb
parentaba9753c0677e860f982edff98c7fe5a2b97758c (diff)
parentb727493011123db329e2901e3abf81a8d146b6fe (diff)
Merge tag 'usb-6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB / Thunderbolt updates from Greg KH: "Here is the big set of USB and Thunderbolt changes for 6.11-rc1. Nothing earth-shattering in here, just constant forward progress in adding support for new hardware and better debugging functionalities for thunderbolt devices and the subsystem. Included in here are: - thunderbolt debugging update and driver additions - xhci driver updates - typec driver updates - kselftest device driver changes (acked by the relevant maintainers, depended on other changes in this tree.) - cdns3 driver updates - gadget driver updates - MODULE_DESCRIPTION() additions - dwc3 driver updates and fixes All of these have been in linux-next for a while with no reported issues" * tag 'usb-6.11-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (112 commits) kselftest: devices: Add test to detect device error logs kselftest: Move ksft helper module to common directory kselftest: devices: Move discoverable devices test to subdirectory usb: gadget: f_uac2: fix non-newline-terminated function name USB: uas: Implement the new shutdown callback USB: core: add 'shutdown' callback to usb_driver usb: typec: Drop explicit initialization of struct i2c_device_id::driver_data to 0 usb: dwc3: enable CCI support for AMD-xilinx DWC3 controller usb: dwc2: add support for other Lantiq SoCs usb: gadget: Use u16 types for 16-bit fields usb: gadget: midi2: Fix incorrect default MIDI2 protocol setup usb: dwc3: core: Check all ports when set phy suspend usb: typec: tcpci: add support to set connector orientation dt-bindings: usb: Convert fsl-usb to yaml usb: typec: ucsi: reorder operations in ucsi_run_command() usb: typec: ucsi: extract common code for command handling usb: typec: ucsi: inline ucsi_read_message_in usb: typec: ucsi: rework command execution functions usb: typec: ucsi: split read operation usb: typec: ucsi: simplify command sending API ...
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/cdns3/cdns3-ti.c15
-rw-r--r--drivers/usb/cdns3/core.h1
-rw-r--r--drivers/usb/cdns3/drd.c10
-rw-r--r--drivers/usb/cdns3/drd.h3
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c19
-rw-r--r--drivers/usb/chipidea/ci_hdrc_msm.c1
-rw-r--r--drivers/usb/chipidea/ci_hdrc_tegra.c9
-rw-r--r--drivers/usb/class/usbtmc.c1
-rw-r--r--drivers/usb/common/common.c1
-rw-r--r--drivers/usb/common/usb-otg-fsm.c1
-rw-r--r--drivers/usb/core/driver.c14
-rw-r--r--drivers/usb/core/usb.c1
-rw-r--r--drivers/usb/dwc2/gadget.c4
-rw-r--r--drivers/usb/dwc2/params.c30
-rw-r--r--drivers/usb/dwc3/core.c66
-rw-r--r--drivers/usb/dwc3/core.h8
-rw-r--r--drivers/usb/dwc3/dwc3-xilinx.c29
-rw-r--r--drivers/usb/dwc3/host.c4
-rw-r--r--drivers/usb/gadget/composite.c1
-rw-r--r--drivers/usb/gadget/function/f_acm.c1
-rw-r--r--drivers/usb/gadget/function/f_ecm.c1
-rw-r--r--drivers/usb/gadget/function/f_eem.c1
-rw-r--r--drivers/usb/gadget/function/f_fs.c1
-rw-r--r--drivers/usb/gadget/function/f_hid.c1
-rw-r--r--drivers/usb/gadget/function/f_loopback.c1
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c1
-rw-r--r--drivers/usb/gadget/function/f_midi.c1
-rw-r--r--drivers/usb/gadget/function/f_midi2.c20
-rw-r--r--drivers/usb/gadget/function/f_ncm.c1
-rw-r--r--drivers/usb/gadget/function/f_obex.c1
-rw-r--r--drivers/usb/gadget/function/f_phonet.c1
-rw-r--r--drivers/usb/gadget/function/f_printer.c1
-rw-r--r--drivers/usb/gadget/function/f_rndis.c1
-rw-r--r--drivers/usb/gadget/function/f_serial.c1
-rw-r--r--drivers/usb/gadget/function/f_sourcesink.c1
-rw-r--r--drivers/usb/gadget/function/f_subset.c1
-rw-r--r--drivers/usb/gadget/function/f_tcm.c1
-rw-r--r--drivers/usb/gadget/function/f_uac1.c1
-rw-r--r--drivers/usb/gadget/function/f_uac1_legacy.c1
-rw-r--r--drivers/usb/gadget/function/f_uac2.c6
-rw-r--r--drivers/usb/gadget/function/f_uvc.c1
-rw-r--r--drivers/usb/gadget/function/storage_common.c1
-rw-r--r--drivers/usb/gadget/function/u_ether.c1
-rw-r--r--drivers/usb/gadget/function/u_serial.c1
-rw-r--r--drivers/usb/gadget/legacy/dbgp.c1
-rw-r--r--drivers/usb/gadget/legacy/gmidi.c1
-rw-r--r--drivers/usb/gadget/legacy/zero.c1
-rw-r--r--drivers/usb/gadget/udc/aspeed_udc.c2
-rw-r--r--drivers/usb/host/ohci-exynos.c1
-rw-r--r--drivers/usb/host/oxu210hp-hcd.c25
-rw-r--r--drivers/usb/host/xhci-dbgcap.c38
-rw-r--r--drivers/usb/host/xhci-dbgcap.h2
-rw-r--r--drivers/usb/host/xhci-mem.c40
-rw-r--r--drivers/usb/host/xhci-pci-renesas.c1
-rw-r--r--drivers/usb/host/xhci-pci.c4
-rw-r--r--drivers/usb/host/xhci-plat.c3
-rw-r--r--drivers/usb/host/xhci-ring.c306
-rw-r--r--drivers/usb/host/xhci-trace.h5
-rw-r--r--drivers/usb/host/xhci.h43
-rw-r--r--drivers/usb/misc/ezusb.c1
-rw-r--r--drivers/usb/misc/isight_firmware.c1
-rw-r--r--drivers/usb/misc/onboard_usb_dev.c11
-rw-r--r--drivers/usb/misc/usb251xb.c18
-rw-r--r--drivers/usb/misc/usb3503.c2
-rw-r--r--drivers/usb/misc/usb4604.c2
-rw-r--r--drivers/usb/misc/yurex.c1
-rw-r--r--drivers/usb/mon/mon_main.c1
-rw-r--r--drivers/usb/musb/da8xx.c20
-rw-r--r--drivers/usb/musb/mpfs.c2
-rw-r--r--drivers/usb/phy/phy-am335x-control.c1
-rw-r--r--drivers/usb/phy/phy-am335x.c1
-rw-r--r--drivers/usb/storage/uas.c7
-rw-r--r--drivers/usb/storage/usb.c101
-rw-r--r--drivers/usb/typec/altmodes/displayport.c2
-rw-r--r--drivers/usb/typec/anx7411.c2
-rw-r--r--drivers/usb/typec/class.c18
-rw-r--r--drivers/usb/typec/mux/gpio-sbu-mux.c6
-rw-r--r--drivers/usb/typec/mux/nb7vpq904m.c97
-rw-r--r--drivers/usb/typec/mux/ptn36502.c33
-rw-r--r--drivers/usb/typec/tcpm/fusb302.c4
-rw-r--r--drivers/usb/typec/tcpm/tcpci.c46
-rw-r--r--drivers/usb/typec/tcpm/tcpci_maxim_core.c2
-rw-r--r--drivers/usb/typec/tcpm/tcpm.c37
-rw-r--r--drivers/usb/typec/tipd/core.c33
-rw-r--r--drivers/usb/typec/ucsi/Kconfig9
-rw-r--r--drivers/usb/typec/ucsi/Makefile1
-rw-r--r--drivers/usb/typec/ucsi/displayport.c2
-rw-r--r--drivers/usb/typec/ucsi/psy.c32
-rw-r--r--drivers/usb/typec/ucsi/ucsi.c284
-rw-r--r--drivers/usb/typec/ucsi/ucsi.h72
-rw-r--r--drivers/usb/typec/ucsi/ucsi_acpi.c127
-rw-r--r--drivers/usb/typec/ucsi/ucsi_ccg.c105
-rw-r--r--drivers/usb/typec/ucsi/ucsi_glink.c74
-rw-r--r--drivers/usb/typec/ucsi/ucsi_stm32g0.c83
-rw-r--r--drivers/usb/typec/ucsi/ucsi_yoga_c630.c164
-rw-r--r--drivers/usb/usbip/stub_rx.c77
96 files changed, 1432 insertions, 787 deletions
diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c
index 5945c4b1e11f..cfabc12ee0e3 100644
--- a/drivers/usb/cdns3/cdns3-ti.c
+++ b/drivers/usb/cdns3/cdns3-ti.c
@@ -16,6 +16,7 @@
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
+#include "core.h"
/* USB Wrapper register offsets */
#define USBSS_PID 0x0
@@ -85,6 +86,18 @@ static inline void cdns_ti_writel(struct cdns_ti *data, u32 offset, u32 value)
writel(value, data->usbss + offset);
}
+static struct cdns3_platform_data cdns_ti_pdata = {
+ .quirks = CDNS3_DRD_SUSPEND_RESIDENCY_ENABLE, /* Errata i2409 */
+};
+
+static const struct of_dev_auxdata cdns_ti_auxdata[] = {
+ {
+ .compatible = "cdns,usb3",
+ .platform_data = &cdns_ti_pdata,
+ },
+ {},
+};
+
static int cdns_ti_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -176,7 +189,7 @@ static int cdns_ti_probe(struct platform_device *pdev)
reg |= USBSS_W1_PWRUP_RST;
cdns_ti_writel(data, USBSS_W1, reg);
- error = of_platform_populate(node, NULL, NULL, dev);
+ error = of_platform_populate(node, NULL, cdns_ti_auxdata, dev);
if (error) {
dev_err(dev, "failed to create children: %d\n", error);
goto err;
diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
index 81a9c9d6be08..57d47348dc19 100644
--- a/drivers/usb/cdns3/core.h
+++ b/drivers/usb/cdns3/core.h
@@ -44,6 +44,7 @@ struct cdns3_platform_data {
bool suspend, bool wakeup);
unsigned long quirks;
#define CDNS3_DEFAULT_PM_RUNTIME_ALLOW BIT(0)
+#define CDNS3_DRD_SUSPEND_RESIDENCY_ENABLE BIT(1)
};
/**
diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
index 8b936a2e93a0..84fb38a5723a 100644
--- a/drivers/usb/cdns3/drd.c
+++ b/drivers/usb/cdns3/drd.c
@@ -389,7 +389,7 @@ static irqreturn_t cdns_drd_irq(int irq, void *data)
int cdns_drd_init(struct cdns *cdns)
{
void __iomem *regs;
- u32 state;
+ u32 state, reg;
int ret;
regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
@@ -433,6 +433,14 @@ int cdns_drd_init(struct cdns *cdns)
cdns->otg_irq_regs = (struct cdns_otg_irq_regs __iomem *)
&cdns->otg_v1_regs->ien;
writel(1, &cdns->otg_v1_regs->simulate);
+
+ if (cdns->pdata &&
+ (cdns->pdata->quirks & CDNS3_DRD_SUSPEND_RESIDENCY_ENABLE)) {
+ reg = readl(&cdns->otg_v1_regs->susp_ctrl);
+ reg |= SUSP_CTRL_SUSPEND_RESIDENCY_ENABLE;
+ writel(reg, &cdns->otg_v1_regs->susp_ctrl);
+ }
+
cdns->version = CDNS3_CONTROLLER_V1;
} else {
dev_err(cdns->dev, "not supported DID=0x%08x\n", state);
diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h
index d72370c321d3..1e2aee14d629 100644
--- a/drivers/usb/cdns3/drd.h
+++ b/drivers/usb/cdns3/drd.h
@@ -193,6 +193,9 @@ struct cdns_otg_irq_regs {
/* OTGREFCLK - bitmasks */
#define OTGREFCLK_STB_CLK_SWITCH_EN BIT(31)
+/* SUPS_CTRL - bitmasks */
+#define SUSP_CTRL_SUSPEND_RESIDENCY_ENABLE BIT(17)
+
/* OVERRIDE - bitmasks */
#define OVERRIDE_IDPULLUP BIT(0)
/* Only for CDNS3_CONTROLLER_V0 version */
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index a17b6d619305..bdc04ce919f7 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -557,7 +557,7 @@ static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
ci_hdrc_imx_remove(pdev);
}
-static int __maybe_unused imx_controller_suspend(struct device *dev,
+static int imx_controller_suspend(struct device *dev,
pm_message_t msg)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
@@ -582,7 +582,7 @@ static int __maybe_unused imx_controller_suspend(struct device *dev,
return 0;
}
-static int __maybe_unused imx_controller_resume(struct device *dev,
+static int imx_controller_resume(struct device *dev,
pm_message_t msg)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
@@ -618,7 +618,7 @@ clk_disable:
return ret;
}
-static int __maybe_unused ci_hdrc_imx_suspend(struct device *dev)
+static int ci_hdrc_imx_suspend(struct device *dev)
{
int ret;
@@ -636,7 +636,7 @@ static int __maybe_unused ci_hdrc_imx_suspend(struct device *dev)
return ret;
}
-static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
+static int ci_hdrc_imx_resume(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret;
@@ -652,7 +652,7 @@ static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
return ret;
}
-static int __maybe_unused ci_hdrc_imx_runtime_suspend(struct device *dev)
+static int ci_hdrc_imx_runtime_suspend(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
@@ -664,15 +664,14 @@ static int __maybe_unused ci_hdrc_imx_runtime_suspend(struct device *dev)
return imx_controller_suspend(dev, PMSG_AUTO_SUSPEND);
}
-static int __maybe_unused ci_hdrc_imx_runtime_resume(struct device *dev)
+static int ci_hdrc_imx_runtime_resume(struct device *dev)
{
return imx_controller_resume(dev, PMSG_AUTO_RESUME);
}
static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
- SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend,
- ci_hdrc_imx_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
+ RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend, ci_hdrc_imx_runtime_resume, NULL)
};
static struct platform_driver ci_hdrc_imx_driver = {
.probe = ci_hdrc_imx_probe,
@@ -681,7 +680,7 @@ static struct platform_driver ci_hdrc_imx_driver = {
.driver = {
.name = "imx_usb",
.of_match_table = ci_hdrc_imx_dt_ids,
- .pm = &ci_hdrc_imx_pm_ops,
+ .pm = pm_ptr(&ci_hdrc_imx_pm_ops),
},
};
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
index 7b5b47ce8a02..1661639cd2eb 100644
--- a/drivers/usb/chipidea/ci_hdrc_msm.c
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -303,4 +303,5 @@ module_platform_driver(ci_hdrc_msm_driver);
MODULE_ALIAS("platform:msm_hsusb");
MODULE_ALIAS("platform:ci13xxx_msm");
+MODULE_DESCRIPTION("ChipIdea Highspeed Dual Role Controller");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c
index 2cc305803217..9538d425f0a0 100644
--- a/drivers/usb/chipidea/ci_hdrc_tegra.c
+++ b/drivers/usb/chipidea/ci_hdrc_tegra.c
@@ -372,7 +372,7 @@ static void tegra_usb_remove(struct platform_device *pdev)
pm_runtime_force_suspend(&pdev->dev);
}
-static int __maybe_unused tegra_usb_runtime_resume(struct device *dev)
+static int tegra_usb_runtime_resume(struct device *dev)
{
struct tegra_usb *usb = dev_get_drvdata(dev);
int err;
@@ -386,7 +386,7 @@ static int __maybe_unused tegra_usb_runtime_resume(struct device *dev)
return 0;
}
-static int __maybe_unused tegra_usb_runtime_suspend(struct device *dev)
+static int tegra_usb_runtime_suspend(struct device *dev)
{
struct tegra_usb *usb = dev_get_drvdata(dev);
@@ -396,15 +396,14 @@ static int __maybe_unused tegra_usb_runtime_suspend(struct device *dev)
}
static const struct dev_pm_ops tegra_usb_pm = {
- SET_RUNTIME_PM_OPS(tegra_usb_runtime_suspend, tegra_usb_runtime_resume,
- NULL)
+ RUNTIME_PM_OPS(tegra_usb_runtime_suspend, tegra_usb_runtime_resume, NULL)
};
static struct platform_driver tegra_usb_driver = {
.driver = {
.name = "tegra-usb",
.of_match_table = tegra_usb_of_match,
- .pm = &tegra_usb_pm,
+ .pm = pm_ptr(&tegra_usb_pm),
},
.probe = tegra_usb_probe,
.remove_new = tegra_usb_remove,
diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c
index 311007b1d904..6bd9fe565385 100644
--- a/drivers/usb/class/usbtmc.c
+++ b/drivers/usb/class/usbtmc.c
@@ -2592,4 +2592,5 @@ static struct usb_driver usbtmc_driver = {
module_usb_driver(usbtmc_driver);
+MODULE_DESCRIPTION("USB Test & Measurement class driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
index b84efae26e15..59b55d6cf490 100644
--- a/drivers/usb/common/common.c
+++ b/drivers/usb/common/common.c
@@ -433,4 +433,5 @@ static void __exit usb_common_exit(void)
subsys_initcall(usb_common_init);
module_exit(usb_common_exit);
+MODULE_DESCRIPTION("Common code for host and device side USB");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
index 0697fde51d00..e11803225775 100644
--- a/drivers/usb/common/usb-otg-fsm.c
+++ b/drivers/usb/common/usb-otg-fsm.c
@@ -449,4 +449,5 @@ int otg_statemachine(struct otg_fsm *fsm)
return fsm->state_changed;
}
EXPORT_SYMBOL_GPL(otg_statemachine);
+MODULE_DESCRIPTION("OTG Finite State Machine");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index e02ba15f6e34..b35734d03109 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -517,6 +517,19 @@ static int usb_unbind_interface(struct device *dev)
return 0;
}
+static void usb_shutdown_interface(struct device *dev)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct usb_driver *driver;
+
+ if (!dev->driver)
+ return;
+
+ driver = to_usb_driver(dev->driver);
+ if (driver->shutdown)
+ driver->shutdown(intf);
+}
+
/**
* usb_driver_claim_interface - bind a driver to an interface
* @driver: the driver to be bound
@@ -1059,6 +1072,7 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner,
new_driver->driver.bus = &usb_bus_type;
new_driver->driver.probe = usb_probe_interface;
new_driver->driver.remove = usb_unbind_interface;
+ new_driver->driver.shutdown = usb_shutdown_interface;
new_driver->driver.owner = owner;
new_driver->driver.mod_name = mod_name;
new_driver->driver.dev_groups = new_driver->dev_groups;
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index a0c432b14b20..0b4685aad2d5 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -1150,4 +1150,5 @@ static void __exit usb_exit(void)
subsys_initcall(usb_init);
module_exit(usb_exit);
+MODULE_DESCRIPTION("USB core host-side support");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 74ac79abd8f3..e7bf9cc635be 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -885,10 +885,10 @@ static void dwc2_gadget_config_nonisoc_xfer_ddma(struct dwc2_hsotg_ep *hs_ep,
}
/* DMA sg buffer */
- for_each_sg(ureq->sg, sg, ureq->num_sgs, i) {
+ for_each_sg(ureq->sg, sg, ureq->num_mapped_sgs, i) {
dwc2_gadget_fill_nonisoc_xfer_ddma_one(hs_ep, &desc,
sg_dma_address(sg) + sg->offset, sg_dma_len(sg),
- sg_is_last(sg));
+ (i == (ureq->num_mapped_sgs - 1)));
desc_count += hs_ep->desc_count;
}
diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index 5a1500d0bdd9..a937eadbc9b3 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -133,7 +133,15 @@ static void dwc2_set_rk_params(struct dwc2_hsotg *hsotg)
p->no_clock_gating = true;
}
-static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg)
+static void dwc2_set_ltq_danube_params(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_core_params *p = &hsotg->params;
+
+ p->otg_caps.hnp_support = false;
+ p->otg_caps.srp_support = false;
+}
+
+static void dwc2_set_ltq_ase_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
@@ -142,12 +150,21 @@ static void dwc2_set_ltq_params(struct dwc2_hsotg *hsotg)
p->host_rx_fifo_size = 288;
p->host_nperio_tx_fifo_size = 128;
p->host_perio_tx_fifo_size = 96;
- p->max_transfer_size = 65535;
- p->max_packet_count = 511;
p->ahbcfg = GAHBCFG_HBSTLEN_INCR16 <<
GAHBCFG_HBSTLEN_SHIFT;
}
+static void dwc2_set_ltq_xrx200_params(struct dwc2_hsotg *hsotg)
+{
+ struct dwc2_core_params *p = &hsotg->params;
+
+ p->otg_caps.hnp_support = false;
+ p->otg_caps.srp_support = false;
+ p->host_rx_fifo_size = 288;
+ p->host_nperio_tx_fifo_size = 128;
+ p->host_perio_tx_fifo_size = 136;
+}
+
static void dwc2_set_amlogic_params(struct dwc2_hsotg *hsotg)
{
struct dwc2_core_params *p = &hsotg->params;
@@ -297,8 +314,11 @@ const struct of_device_id dwc2_of_match_table[] = {
{ .compatible = "ingenic,x1830-otg", .data = dwc2_set_x1600_params },
{ .compatible = "ingenic,x2000-otg", .data = dwc2_set_x2000_params },
{ .compatible = "rockchip,rk3066-usb", .data = dwc2_set_rk_params },
- { .compatible = "lantiq,arx100-usb", .data = dwc2_set_ltq_params },
- { .compatible = "lantiq,xrx200-usb", .data = dwc2_set_ltq_params },
+ { .compatible = "lantiq,danube-usb", .data = &dwc2_set_ltq_danube_params },
+ { .compatible = "lantiq,ase-usb", .data = &dwc2_set_ltq_ase_params },
+ { .compatible = "lantiq,arx100-usb", .data = &dwc2_set_ltq_ase_params },
+ { .compatible = "lantiq,xrx200-usb", .data = &dwc2_set_ltq_xrx200_params },
+ { .compatible = "lantiq,xrx300-usb", .data = &dwc2_set_ltq_xrx200_params },
{ .compatible = "snps,dwc2" },
{ .compatible = "samsung,s3c6400-hsotg",
.data = dwc2_set_s3c6400_params },
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index cb82557678dd..734de2a8bd21 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -108,22 +108,27 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
void dwc3_enable_susphy(struct dwc3 *dwc, bool enable)
{
u32 reg;
+ int i;
- reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
- if (enable && !dwc->dis_u3_susphy_quirk)
- reg |= DWC3_GUSB3PIPECTL_SUSPHY;
- else
- reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
+ for (i = 0; i < dwc->num_usb3_ports; i++) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(i));
+ if (enable && !dwc->dis_u3_susphy_quirk)
+ reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+ else
+ reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(i), reg);
+ }
- reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
- if (enable && !dwc->dis_u2_susphy_quirk)
- reg |= DWC3_GUSB2PHYCFG_SUSPHY;
- else
- reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+ for (i = 0; i < dwc->num_usb2_ports; i++) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(i));
+ if (enable && !dwc->dis_u2_susphy_quirk)
+ reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+ else
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
- dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(i), reg);
+ }
}
void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode)
@@ -599,6 +604,18 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
parms->hwparams9 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS9);
}
+static void dwc3_config_soc_bus(struct dwc3 *dwc)
+{
+ if (dwc->gsbuscfg0_reqinfo != DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED) {
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
+ reg &= ~DWC3_GSBUSCFG0_REQINFO(~0);
+ reg |= DWC3_GSBUSCFG0_REQINFO(dwc->gsbuscfg0_reqinfo);
+ dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, reg);
+ }
+}
+
static int dwc3_core_ulpi_init(struct dwc3 *dwc)
{
int intf;
@@ -1338,6 +1355,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_set_incr_burst_type(dwc);
+ dwc3_config_soc_bus(dwc);
+
ret = dwc3_phy_power_on(dwc);
if (ret)
goto err_exit_phy;
@@ -1576,6 +1595,27 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc)
dwc3_set_prtcap(dwc, DWC3_GCTL_PRTCAP_DEVICE);
}
+static void dwc3_get_software_properties(struct dwc3 *dwc)
+{
+ struct device *tmpdev;
+ u16 gsbuscfg0_reqinfo;
+ int ret;
+
+ dwc->gsbuscfg0_reqinfo = DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED;
+
+ /*
+ * Iterate over all parent nodes for finding swnode properties
+ * and non-DT (non-ABI) properties.
+ */
+ for (tmpdev = dwc->dev; tmpdev; tmpdev = tmpdev->parent) {
+ ret = device_property_read_u16(tmpdev,
+ "snps,gsbuscfg0-reqinfo",
+ &gsbuscfg0_reqinfo);
+ if (!ret)
+ dwc->gsbuscfg0_reqinfo = gsbuscfg0_reqinfo;
+ }
+}
+
static void dwc3_get_properties(struct dwc3 *dwc)
{
struct device *dev = dwc->dev;
@@ -2090,6 +2130,8 @@ static int dwc3_probe(struct platform_device *pdev)
dwc3_get_properties(dwc);
+ dwc3_get_software_properties(dwc);
+
dwc->reset = devm_reset_control_array_get_optional_shared(dev);
if (IS_ERR(dwc->reset)) {
ret = PTR_ERR(dwc->reset);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 3781c736c1a1..1e561fd8b86e 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -194,6 +194,10 @@
#define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */
#define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff
+/* Global SoC Bus Configuration Register: AHB-prot/AXI-cache/OCP-ReqInfo */
+#define DWC3_GSBUSCFG0_REQINFO(n) (((n) & 0xffff) << 16)
+#define DWC3_GSBUSCFG0_REQINFO_UNSPECIFIED 0xffffffff
+
/* Global Debug LSP MUX Select */
#define DWC3_GDBGLSPMUX_ENDBC BIT(15) /* Host only */
#define DWC3_GDBGLSPMUX_HOSTSELECT(n) ((n) & 0x3fff)
@@ -1153,6 +1157,9 @@ struct dwc3_scratchpad_array {
* @num_ep_resized: carries the current number endpoints which have had its tx
* fifo resized.
* @debug_root: root debugfs directory for this device to put its files in.
+ * @gsbuscfg0_reqinfo: store GSBUSCFG0.DATRDREQINFO, DESRDREQINFO,
+ * DATWRREQINFO, and DESWRREQINFO value passed from
+ * glue driver.
*/
struct dwc3 {
struct work_struct drd_work;
@@ -1380,6 +1387,7 @@ struct dwc3 {
int last_fifo_depth;
int num_ep_resized;
struct dentry *debug_root;
+ u32 gsbuscfg0_reqinfo;
};
#define INCRX_BURST_MODE 0
diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c
index 6095f4dee6ce..bb4d894c16e9 100644
--- a/drivers/usb/dwc3/dwc3-xilinx.c
+++ b/drivers/usb/dwc3/dwc3-xilinx.c
@@ -246,6 +246,31 @@ static const struct of_device_id dwc3_xlnx_of_match[] = {
};
MODULE_DEVICE_TABLE(of, dwc3_xlnx_of_match);
+static int dwc3_set_swnode(struct device *dev)
+{
+ struct device_node *np = dev->of_node, *dwc3_np;
+ struct property_entry props[2];
+ int prop_idx = 0, ret = 0;
+
+ dwc3_np = of_get_compatible_child(np, "snps,dwc3");
+ if (!dwc3_np) {
+ ret = -ENODEV;
+ dev_err(dev, "failed to find dwc3 core child\n");
+ return ret;
+ }
+
+ memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
+ if (of_dma_is_coherent(dwc3_np))
+ props[prop_idx++] = PROPERTY_ENTRY_U16("snps,gsbuscfg0-reqinfo",
+ 0xffff);
+ of_node_put(dwc3_np);
+
+ if (prop_idx)
+ ret = device_create_managed_software_node(dev, props, NULL);
+
+ return ret;
+}
+
static int dwc3_xlnx_probe(struct platform_device *pdev)
{
struct dwc3_xlnx *priv_data;
@@ -288,6 +313,10 @@ static int dwc3_xlnx_probe(struct platform_device *pdev)
if (ret)
goto err_clk_put;
+ ret = dwc3_set_swnode(dev);
+ if (ret)
+ goto err_clk_put;
+
ret = of_platform_populate(np, NULL, NULL, dev);
if (ret)
goto err_clk_put;
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index a171b27a7845..e0533cee6870 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -126,7 +126,7 @@ out:
int dwc3_host_init(struct dwc3 *dwc)
{
- struct property_entry props[5];
+ struct property_entry props[6];
struct platform_device *xhci;
int ret, irq;
int prop_idx = 0;
@@ -162,6 +162,8 @@ int dwc3_host_init(struct dwc3 *dwc)
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-sg-trb-cache-size-quirk");
+ props[prop_idx++] = PROPERTY_ENTRY_BOOL("write-64-hi-lo-quirk");
+
if (dwc->usb3_lpm_capable)
props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb3-lpm-capable");
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 0e151b54aae8..f45d5bedda68 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -2799,5 +2799,6 @@ void usb_composite_overwrite_options(struct usb_composite_dev *cdev,
}
EXPORT_SYMBOL_GPL(usb_composite_overwrite_options);
+MODULE_DESCRIPTION("infrastructure for Composite USB Gadgets");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c
index f616059c5e1e..724b2631f249 100644
--- a/drivers/usb/gadget/function/f_acm.c
+++ b/drivers/usb/gadget/function/f_acm.c
@@ -854,4 +854,5 @@ static struct usb_function_instance *acm_alloc_instance(void)
return &opts->func_inst;
}
DECLARE_USB_FUNCTION_INIT(acm, acm_alloc_instance, acm_alloc_func);
+MODULE_DESCRIPTION("USB CDC serial (ACM) function driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c
index f55f60639e42..6cb7771e8a69 100644
--- a/drivers/usb/gadget/function/f_ecm.c
+++ b/drivers/usb/gadget/function/f_ecm.c
@@ -966,5 +966,6 @@ static struct usb_function *ecm_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc);
+MODULE_DESCRIPTION("USB CDC Ethernet (ECM) link function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c
index 3b445bd88498..6de81ea17274 100644
--- a/drivers/usb/gadget/function/f_eem.c
+++ b/drivers/usb/gadget/function/f_eem.c
@@ -674,5 +674,6 @@ static struct usb_function *eem_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(eem, eem_alloc_inst, eem_alloc);
+MODULE_DESCRIPTION("USB CDC Ethernet (EEM) link function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 1f21459b1188..d8b096859337 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -4316,5 +4316,6 @@ static char *ffs_prepare_buffer(const char __user *buf, size_t len)
}
DECLARE_USB_FUNCTION_INIT(ffs, ffs_alloc_inst, ffs_alloc);
+MODULE_DESCRIPTION("user mode file system API for USB composite function controllers");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michal Nazarewicz");
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index 2db01e03bfbf..93dae017ae45 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -1322,6 +1322,7 @@ err_unlock:
}
DECLARE_USB_FUNCTION_INIT(hid, hidg_alloc_inst, hidg_alloc);
+MODULE_DESCRIPTION("USB HID function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Fabien Chouteau");
diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c
index 17ac6ace0cff..979b028edb99 100644
--- a/drivers/usb/gadget/function/f_loopback.c
+++ b/drivers/usb/gadget/function/f_loopback.c
@@ -593,4 +593,5 @@ void __exit lb_modexit(void)
usb_function_unregister(&Loopbackusb_func);
}
+MODULE_DESCRIPTION("USB peripheral loopback configuration driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index c265a1f62fc1..cfd712fd7452 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -3577,6 +3577,7 @@ static struct usb_function *fsg_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(mass_storage, fsg_alloc_inst, fsg_alloc);
+MODULE_DESCRIPTION("Mass Storage USB Composite Function");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Michal Nazarewicz");
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 20c6fbd94f32..67052a664e74 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -38,6 +38,7 @@
#include "u_midi.h"
MODULE_AUTHOR("Ben Williamson");
+MODULE_DESCRIPTION("USB MIDI class function driver");
MODULE_LICENSE("GPL v2");
static const char f_midi_shortname[] = "f_midi";
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
index ec8cd7c7bbfc..38e8ed3144f0 100644
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -150,6 +150,9 @@ struct f_midi2 {
#define func_to_midi2(f) container_of(f, struct f_midi2, func)
+/* convert from MIDI protocol number (1 or 2) to SNDRV_UMP_EP_INFO_PROTO_* */
+#define to_ump_protocol(v) (((v) & 3) << 8)
+
/* get EP name string */
static const char *ump_ep_name(const struct f_midi2_ep *ep)
{
@@ -564,8 +567,7 @@ static void reply_ump_stream_ep_config(struct f_midi2_ep *ep)
.status = UMP_STREAM_MSG_STATUS_STREAM_CFG,
};
- if ((ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK) ==
- SNDRV_UMP_EP_INFO_PROTO_MIDI2)
+ if (ep->info.protocol == 2)
rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI2 >> 8;
else
rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 >> 8;
@@ -627,13 +629,13 @@ static void process_ump_stream_msg(struct f_midi2_ep *ep, const u32 *data)
return;
case UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST:
if (*data & UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) {
- ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI2;
+ ep->info.protocol = 2;
DBG(midi2, "Switching Protocol to MIDI2\n");
} else {
- ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI1;
+ ep->info.protocol = 1;
DBG(midi2, "Switching Protocol to MIDI1\n");
}
- snd_ump_switch_protocol(ep->ump, ep->info.protocol);
+ snd_ump_switch_protocol(ep->ump, to_ump_protocol(ep->info.protocol));
reply_ump_stream_ep_config(ep);
return;
case UMP_STREAM_MSG_STATUS_FB_DISCOVERY:
@@ -1065,7 +1067,8 @@ static void f_midi2_midi1_ep_out_complete(struct usb_ep *usb_ep,
group = midi2->out_cable_mapping[cable].group;
bytes = midi1_packet_bytes[*buf & 0x0f];
for (c = 0; c < bytes; c++) {
- snd_ump_convert_to_ump(cvt, group, ep->info.protocol,
+ snd_ump_convert_to_ump(cvt, group,
+ to_ump_protocol(ep->info.protocol),
buf[c + 1]);
if (cvt->ump_bytes) {
snd_ump_receive(ep->ump, cvt->ump,
@@ -1375,7 +1378,7 @@ static void assign_block_descriptors(struct f_midi2 *midi2,
desc->nNumGroupTrm = b->num_groups;
desc->iBlockItem = ep->blks[blk].string_id;
- if (ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2)
+ if (ep->info.protocol == 2)
desc->bMIDIProtocol = USB_MS_MIDI_PROTO_2_0;
else
desc->bMIDIProtocol = USB_MS_MIDI_PROTO_1_0_128;
@@ -1552,7 +1555,7 @@ static int f_midi2_create_card(struct f_midi2 *midi2)
if (midi2->info.static_block)
ump->info.flags |= SNDRV_UMP_EP_INFO_STATIC_BLOCKS;
ump->info.protocol_caps = (ep->info.protocol_caps & 3) << 8;
- ump->info.protocol = (ep->info.protocol & 3) << 8;
+ ump->info.protocol = to_ump_protocol(ep->info.protocol);
ump->info.version = 0x0101;
ump->info.family_id = ep->info.family;
ump->info.model_id = ep->info.model;
@@ -2868,4 +2871,5 @@ static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi)
DECLARE_USB_FUNCTION_INIT(midi2, f_midi2_alloc_inst, f_midi2_alloc);
+MODULE_DESCRIPTION("USB MIDI 2.0 class function driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 0acc32ed9960..8e761249d672 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -1797,5 +1797,6 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(ncm, ncm_alloc_inst, ncm_alloc);
+MODULE_DESCRIPTION("USB CDC Network (NCM) link function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yauheni Kaliuta");
diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c
index dcb093210305..1305e2326cdf 100644
--- a/drivers/usb/gadget/function/f_obex.c
+++ b/drivers/usb/gadget/function/f_obex.c
@@ -487,4 +487,5 @@ static struct usb_function *obex_alloc(struct usb_function_instance *fi)
DECLARE_USB_FUNCTION_INIT(obex, obex_alloc_inst, obex_alloc);
MODULE_AUTHOR("Felipe Balbi");
+MODULE_DESCRIPTION("USB CDC OBEX function driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c
index 0bebbdf3f213..0aa9e8224cae 100644
--- a/drivers/usb/gadget/function/f_phonet.c
+++ b/drivers/usb/gadget/function/f_phonet.c
@@ -729,4 +729,5 @@ void gphonet_cleanup(struct net_device *dev)
DECLARE_USB_FUNCTION_INIT(phonet, phonet_alloc_inst, phonet_alloc);
MODULE_AUTHOR("Rémi Denis-Courmont");
+MODULE_DESCRIPTION("USB CDC Phonet function");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c
index 44e20c6c36d3..ef2ffde625c3 100644
--- a/drivers/usb/gadget/function/f_printer.c
+++ b/drivers/usb/gadget/function/f_printer.c
@@ -1527,6 +1527,7 @@ static struct usb_function *gprinter_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(printer, gprinter_alloc_inst, gprinter_alloc);
+MODULE_DESCRIPTION("USB printer function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Craig Nadler");
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index b47f99d17ee9..7cec19d65fb5 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -1013,5 +1013,6 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc);
+MODULE_DESCRIPTION("RNDIS link function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c
index 65c50092aea2..8f7e7a2b2ff2 100644
--- a/drivers/usb/gadget/function/f_serial.c
+++ b/drivers/usb/gadget/function/f_serial.c
@@ -392,6 +392,7 @@ static struct usb_function *gser_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(gser, gser_alloc_inst, gser_alloc);
+MODULE_DESCRIPTION("generic USB serial function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Al Borchers");
MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index 2edbd9b510d6..6f3702210450 100644
--- a/drivers/usb/gadget/function/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
@@ -1284,4 +1284,5 @@ static void __exit sslb_modexit(void)
module_init(sslb_modinit);
module_exit(sslb_modexit);
+MODULE_DESCRIPTION("USB peripheral source/sink configuration driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c
index 8ae9689ef2a0..ea3fdd842462 100644
--- a/drivers/usb/gadget/function/f_subset.c
+++ b/drivers/usb/gadget/function/f_subset.c
@@ -500,5 +500,6 @@ static struct usb_function *geth_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(geth, geth_alloc_inst, geth_alloc);
+MODULE_DESCRIPTION("\"CDC Subset\" Ethernet link function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c
index 37befd6db001..90906d714736 100644
--- a/drivers/usb/gadget/function/f_tcm.c
+++ b/drivers/usb/gadget/function/f_tcm.c
@@ -2301,5 +2301,6 @@ static void __exit tcm_exit(void)
}
module_exit(tcm_exit);
+MODULE_DESCRIPTION("Target based USB-Gadget");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sebastian Andrzej Siewior");
diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 7de74a3dd392..2b9fb4daa806 100644
--- a/drivers/usb/gadget/function/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
@@ -1823,5 +1823,6 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(uac1, f_audio_alloc_inst, f_audio_alloc);
+MODULE_DESCRIPTION("USB Audio Class 1.0 Function (using u_audio API)");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ruslan Bilovol");
diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c
index e2d7f69128a0..49cf5aae90ca 100644
--- a/drivers/usb/gadget/function/f_uac1_legacy.c
+++ b/drivers/usb/gadget/function/f_uac1_legacy.c
@@ -1014,5 +1014,6 @@ static struct usb_function *f_audio_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(uac1_legacy, f_audio_alloc_inst, f_audio_alloc);
+MODULE_DESCRIPTION("USB Audio class function driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Bryan Wu");
diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 383f6854cfec..2d6d3286ffde 100644
--- a/drivers/usb/gadget/function/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -2063,7 +2063,10 @@ static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \
goto end; \
} \
\
- ret = scnprintf(opts->name, min(sizeof(opts->name), len), \
+ if (len && page[len - 1] == '\n') \
+ len--; \
+ \
+ ret = scnprintf(opts->name, min(sizeof(opts->name), len + 1), \
"%s", page); \
\
end: \
@@ -2251,6 +2254,7 @@ static struct usb_function *afunc_alloc(struct usb_function_instance *fi)
}
DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc);
+MODULE_DESCRIPTION("USB Audio Class 2.0 Function");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Yadwinder Singh");
MODULE_AUTHOR("Jaswinder Singh");
diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index 929666805bd2..40187b7112e7 100644
--- a/drivers/usb/gadget/function/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
@@ -1118,5 +1118,6 @@ err_config:
}
DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, uvc_alloc);
+MODULE_DESCRIPTION("USB Video Class Gadget driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Laurent Pinchart");
diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c
index 2a4163b0f6fe..75831f2c7abe 100644
--- a/drivers/usb/gadget/function/storage_common.c
+++ b/drivers/usb/gadget/function/storage_common.c
@@ -537,4 +537,5 @@ ssize_t fsg_store_forced_eject(struct fsg_lun *curlun, struct rw_semaphore *file
}
EXPORT_SYMBOL_GPL(fsg_store_forced_eject);
+MODULE_DESCRIPTION("Common definitions for mass storage functionality");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 95191083b455..09e2838917e2 100644
--- a/drivers/usb/gadget/function/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -1245,5 +1245,6 @@ void gether_disconnect(struct gether *link)
}
EXPORT_SYMBOL_GPL(gether_disconnect);
+MODULE_DESCRIPTION("Ethernet-over-USB link layer utilities for Gadget stack");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index a92eb6d90976..eec7f7a2e40f 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -1536,4 +1536,5 @@ static void __exit userial_cleanup(void)
}
module_exit(userial_cleanup);
+MODULE_DESCRIPTION("utilities for USB gadget \"serial port\"/TTY support");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c
index b62e45235e8e..d70fb5bc2357 100644
--- a/drivers/usb/gadget/legacy/dbgp.c
+++ b/drivers/usb/gadget/legacy/dbgp.c
@@ -434,6 +434,7 @@ static void __exit dbgp_exit(void)
}
MODULE_AUTHOR("Stephane Duverger");
+MODULE_DESCRIPTION("EHCI Debug Port device gadget");
MODULE_LICENSE("GPL");
module_init(dbgp_init);
module_exit(dbgp_exit);
diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c
index 265c392810d7..e4a419b19f45 100644
--- a/drivers/usb/gadget/legacy/gmidi.c
+++ b/drivers/usb/gadget/legacy/gmidi.c
@@ -31,6 +31,7 @@
/*-------------------------------------------------------------------------*/
MODULE_AUTHOR("Ben Williamson");
+MODULE_DESCRIPTION("USB MIDI Gadget Driver");
MODULE_LICENSE("GPL v2");
static const char longname[] = "MIDI Gadget";
diff --git a/drivers/usb/gadget/legacy/zero.c b/drivers/usb/gadget/legacy/zero.c
index 23312a07efb4..e25e0d8dd387 100644
--- a/drivers/usb/gadget/legacy/zero.c
+++ b/drivers/usb/gadget/legacy/zero.c
@@ -425,4 +425,5 @@ static struct usb_composite_driver zero_driver = {
module_usb_composite_driver(zero_driver);
MODULE_AUTHOR("David Brownell");
+MODULE_DESCRIPTION("Gadget Zero, for USB development");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c
index 821a6ab5da56..f4781e611aaa 100644
--- a/drivers/usb/gadget/udc/aspeed_udc.c
+++ b/drivers/usb/gadget/udc/aspeed_udc.c
@@ -1009,6 +1009,8 @@ static void ast_udc_getstatus(struct ast_udc_dev *udc)
break;
case USB_RECIP_ENDPOINT:
epnum = crq.wIndex & USB_ENDPOINT_NUMBER_MASK;
+ if (epnum >= AST_UDC_NUM_ENDPOINTS)
+ goto stall;
status = udc->ep[epnum].stopped;
break;
default:
diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c
index 3c4d68fd5c33..bfa2eba4e3a7 100644
--- a/drivers/usb/host/ohci-exynos.c
+++ b/drivers/usb/host/ohci-exynos.c
@@ -293,4 +293,5 @@ module_exit(ohci_exynos_cleanup);
MODULE_ALIAS("platform:exynos-ohci");
MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
+MODULE_DESCRIPTION("OHCI support for Samsung S5P/Exynos SoC Series");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c
index d467472f9d3c..3f871fe62b90 100644
--- a/drivers/usb/host/oxu210hp-hcd.c
+++ b/drivers/usb/host/oxu210hp-hcd.c
@@ -196,31 +196,6 @@ struct ehci_regs {
#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
} __packed;
-/* Appendix C, Debug port ... intended for use with special "debug devices"
- * that can help if there's no serial console. (nonstandard enumeration.)
- */
-struct ehci_dbg_port {
- u32 control;
-#define DBGP_OWNER (1<<30)
-#define DBGP_ENABLED (1<<28)
-#define DBGP_DONE (1<<16)
-#define DBGP_INUSE (1<<10)
-#define DBGP_ERRCODE(x) (((x)>>7)&0x07)
-# define DBGP_ERR_BAD 1
-# define DBGP_ERR_SIGNAL 2
-#define DBGP_ERROR (1<<6)
-#define DBGP_GO (1<<5)
-#define DBGP_OUT (1<<4)
-#define DBGP_LEN(x) (((x)>>0)&0x0f)
- u32 pids;
-#define DBGP_PID_GET(x) (((x)>>16)&0xff)
-#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok))
- u32 data03;
- u32 data47;
- u32 address;
-#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep))
-} __packed;
-
#define QTD_NEXT(dma) cpu_to_le32((u32)dma)
/*
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
index 872d9cddbcef..161c09953c4e 100644
--- a/drivers/usb/host/xhci-dbgcap.c
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -1150,11 +1150,48 @@ static ssize_t dbc_bInterfaceProtocol_store(struct device *dev,
return size;
}
+static ssize_t dbc_poll_interval_ms_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct xhci_dbc *dbc;
+ struct xhci_hcd *xhci;
+
+ xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ dbc = xhci->dbc;
+
+ return sysfs_emit(buf, "%u\n", dbc->poll_interval);
+}
+
+static ssize_t dbc_poll_interval_ms_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct xhci_dbc *dbc;
+ struct xhci_hcd *xhci;
+ u32 value;
+ int ret;
+
+ ret = kstrtou32(buf, 0, &value);
+ if (ret || value > DBC_POLL_INTERVAL_MAX)
+ return -EINVAL;
+
+ xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ dbc = xhci->dbc;
+
+ dbc->poll_interval = value;
+
+ mod_delayed_work(system_wq, &dbc->event_work, 0);
+
+ return size;
+}
+
static DEVICE_ATTR_RW(dbc);
static DEVICE_ATTR_RW(dbc_idVendor);
static DEVICE_ATTR_RW(dbc_idProduct);
static DEVICE_ATTR_RW(dbc_bcdDevice);
static DEVICE_ATTR_RW(dbc_bInterfaceProtocol);
+static DEVICE_ATTR_RW(dbc_poll_interval_ms);
static struct attribute *dbc_dev_attrs[] = {
&dev_attr_dbc.attr,
@@ -1162,6 +1199,7 @@ static struct attribute *dbc_dev_attrs[] = {
&dev_attr_dbc_idProduct.attr,
&dev_attr_dbc_bcdDevice.attr,
&dev_attr_dbc_bInterfaceProtocol.attr,
+ &dev_attr_dbc_poll_interval_ms.attr,
NULL
};
ATTRIBUTE_GROUPS(dbc_dev);
diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h
index 92661b555c2a..0118c6288a3c 100644
--- a/drivers/usb/host/xhci-dbgcap.h
+++ b/drivers/usb/host/xhci-dbgcap.h
@@ -95,7 +95,7 @@ struct dbc_ep {
#define DBC_QUEUE_SIZE 16
#define DBC_WRITE_BUF_SIZE 8192
#define DBC_POLL_INTERVAL_DEFAULT 64 /* milliseconds */
-
+#define DBC_POLL_INTERVAL_MAX 5000 /* milliseconds */
/*
* Private structure for DbC hardware state:
*/
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 3100219d6496..d7654f475daf 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -136,10 +136,7 @@ static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring,
if (!ring || !first || !last)
return;
- /* Set chain bit for 0.95 hosts, and for isoc rings on AMD 0.96 host */
- chain_links = !!(xhci_link_trb_quirk(xhci) ||
- (ring->type == TYPE_ISOC &&
- (xhci->quirks & XHCI_AMD_0x96_HOST)));
+ chain_links = xhci_link_chain_quirk(xhci, ring->type);
next = ring->enq_seg->next;
xhci_link_segments(ring->enq_seg, first, ring->type, chain_links);
@@ -156,7 +153,7 @@ static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring,
ring->last_seg = last;
}
- for (seg = last; seg != ring->last_seg; seg = seg->next)
+ for (seg = ring->enq_seg; seg != ring->last_seg; seg = seg->next)
seg->next->num = seg->num + 1;
}
@@ -327,18 +324,19 @@ EXPORT_SYMBOL_GPL(xhci_initialize_ring_info);
/* Allocate segments and link them for a ring */
static int xhci_alloc_segments_for_ring(struct xhci_hcd *xhci,
- struct xhci_segment **first, struct xhci_segment **last,
- unsigned int num_segs, unsigned int num,
- unsigned int cycle_state, enum xhci_ring_type type,
- unsigned int max_packet, gfp_t flags)
+ struct xhci_segment **first,
+ struct xhci_segment **last,
+ unsigned int num_segs,
+ unsigned int cycle_state,
+ enum xhci_ring_type type,
+ unsigned int max_packet,
+ gfp_t flags)
{
struct xhci_segment *prev;
+ unsigned int num = 0;
bool chain_links;
- /* Set chain bit for 0.95 hosts, and for isoc rings on AMD 0.96 host */
- chain_links = !!(xhci_link_trb_quirk(xhci) ||
- (type == TYPE_ISOC &&
- (xhci->quirks & XHCI_AMD_0x96_HOST)));
+ chain_links = xhci_link_chain_quirk(xhci, type);
prev = xhci_segment_alloc(xhci, cycle_state, max_packet, num, flags);
if (!prev)
@@ -394,9 +392,8 @@ struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci,
if (num_segs == 0)
return ring;
- ret = xhci_alloc_segments_for_ring(xhci, &ring->first_seg,
- &ring->last_seg, num_segs, 0, cycle_state, type,
- max_packet, flags);
+ ret = xhci_alloc_segments_for_ring(xhci, &ring->first_seg, &ring->last_seg, num_segs,
+ cycle_state, type, max_packet, flags);
if (ret)
goto fail;
@@ -434,10 +431,8 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
struct xhci_segment *last;
int ret;
- ret = xhci_alloc_segments_for_ring(xhci, &first, &last,
- num_new_segs, ring->enq_seg->num + 1,
- ring->cycle_state, ring->type,
- ring->bounce_buf_len, flags);
+ ret = xhci_alloc_segments_for_ring(xhci, &first, &last, num_new_segs, ring->cycle_state,
+ ring->type, ring->bounce_buf_len, flags);
if (ret)
return -ENOMEM;
@@ -2325,7 +2320,10 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base);
erst_base &= ERST_BASE_RSVDP;
erst_base |= ir->erst.erst_dma_addr & ~ERST_BASE_RSVDP;
- xhci_write_64(xhci, erst_base, &ir->ir_set->erst_base);
+ if (xhci->quirks & XHCI_WRITE_64_HI_LO)
+ hi_lo_writeq(erst_base, &ir->ir_set->erst_base);
+ else
+ xhci_write_64(xhci, erst_base, &ir->ir_set->erst_base);
/* Set the event ring dequeue address of this interrupter */
xhci_set_hc_event_deq(xhci, ir);
diff --git a/drivers/usb/host/xhci-pci-renesas.c b/drivers/usb/host/xhci-pci-renesas.c
index 93f8b355bc70..247cc7c2ce70 100644
--- a/drivers/usb/host/xhci-pci-renesas.c
+++ b/drivers/usb/host/xhci-pci-renesas.c
@@ -627,4 +627,5 @@ exit:
}
EXPORT_SYMBOL_GPL(renesas_xhci_check_request_fw);
+MODULE_DESCRIPTION("Support for Renesas xHCI controller with firmware");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 05881153883e..dc1e345ab67e 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -50,6 +50,7 @@
#define PCI_DEVICE_ID_INTEL_DENVERTON_XHCI 0x19d0
#define PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI 0x8a13
#define PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI 0x9a13
+#define PCI_DEVICE_ID_INTEL_TIGER_LAKE_PCH_XHCI 0xa0ed
#define PCI_DEVICE_ID_INTEL_COMET_LAKE_XHCI 0xa3af
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI 0x51ed
#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_N_PCH_XHCI 0x54ed
@@ -373,7 +374,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
xhci->quirks |= XHCI_MISSING_CAS;
if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
- (pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI ||
+ (pdev->device == PCI_DEVICE_ID_INTEL_TIGER_LAKE_PCH_XHCI ||
+ pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI ||
pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_N_PCH_XHCI))
xhci->quirks |= XHCI_RESET_TO_DEFAULT;
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 3d071b875308..31bdfa52eeb2 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -256,6 +256,9 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
if (device_property_read_bool(tmpdev, "xhci-sg-trb-cache-size-quirk"))
xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK;
+ if (device_property_read_bool(tmpdev, "write-64-hi-lo-quirk"))
+ xhci->quirks |= XHCI_WRITE_64_HI_LO;
+
device_property_read_u32(tmpdev, "imod-interval-ns",
&xhci->imod_interval);
}
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index fd0cde3d1569..b7517c3c8059 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -250,9 +250,7 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
* AMD 0.96 host, carry over the chain bit of the previous TRB
* (which may mean the chain bit is cleared).
*/
- if (!(ring->type == TYPE_ISOC &&
- (xhci->quirks & XHCI_AMD_0x96_HOST)) &&
- !xhci_link_trb_quirk(xhci)) {
+ if (!xhci_link_chain_quirk(xhci, ring->type)) {
next->link.control &= cpu_to_le32(~TRB_CHAIN);
next->link.control |= cpu_to_le32(chain);
}
@@ -283,7 +281,7 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring,
* Only for transfer and command rings where driver is the producer, not for
* event rings.
*/
-static unsigned int xhci_num_trbs_free(struct xhci_hcd *xhci, struct xhci_ring *ring)
+static unsigned int xhci_num_trbs_free(struct xhci_ring *ring)
{
struct xhci_segment *enq_seg = ring->enq_seg;
union xhci_trb *enq = ring->enqueue;
@@ -654,25 +652,6 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
stream_id);
return -ENODEV;
}
- /*
- * A cancelled TD can complete with a stall if HW cached the trb.
- * In this case driver can't find td, but if the ring is empty we
- * can move the dequeue pointer to the current enqueue position.
- * We shouldn't hit this anymore as cached cancelled TRBs are given back
- * after clearing the cache, but be on the safe side and keep it anyway
- */
- if (!td) {
- if (list_empty(&ep_ring->td_list)) {
- new_seg = ep_ring->enq_seg;
- new_deq = ep_ring->enqueue;
- new_cycle = ep_ring->cycle_state;
- xhci_dbg(xhci, "ep ring empty, Set new dequeue = enqueue");
- goto deq_found;
- } else {
- xhci_warn(xhci, "Can't find new dequeue state, missing td\n");
- return -EINVAL;
- }
- }
hw_dequeue = xhci_get_hw_deq(xhci, dev, ep_index, stream_id);
new_seg = ep_ring->deq_seg;
@@ -709,8 +688,6 @@ static int xhci_move_dequeue_past_td(struct xhci_hcd *xhci,
} while (!cycle_found || !td_last_trb_found);
-deq_found:
-
/* Don't update the ring cycle state for the producer (us). */
addr = xhci_trb_virt_to_dma(new_seg, new_deq);
if (addr == 0) {
@@ -738,7 +715,7 @@ deq_found:
lower_32_bits(addr) | trb_sct | new_cycle,
upper_32_bits(addr),
STREAM_ID_FOR_TRB(stream_id), SLOT_ID_FOR_TRB(slot_id) |
- EP_ID_FOR_TRB(ep_index) | TRB_TYPE(TRB_SET_DEQ), false);
+ EP_INDEX_FOR_TRB(ep_index) | TRB_TYPE(TRB_SET_DEQ), false);
if (ret < 0) {
xhci_free_command(xhci, cmd);
return ret;
@@ -783,10 +760,6 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
}
}
-/*
- * Must be called with xhci->lock held in interrupt context,
- * releases and re-acquires xhci->lock
- */
static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
struct xhci_td *cur_td, int status)
{
@@ -1511,8 +1484,8 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
}
-static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci, int slot_id,
- struct xhci_command *command, u32 cmd_comp_code)
+static void xhci_handle_cmd_enable_slot(int slot_id, struct xhci_command *command,
+ u32 cmd_comp_code)
{
if (cmd_comp_code == COMP_SUCCESS)
command->slot_id = slot_id;
@@ -1537,8 +1510,7 @@ static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id)
xhci_free_device_endpoint_resources(xhci, virt_dev, true);
}
-static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id,
- u32 cmd_comp_code)
+static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id)
{
struct xhci_virt_device *virt_dev;
struct xhci_input_control_ctx *ctrl_ctx;
@@ -1780,14 +1752,14 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
cmd_type = TRB_FIELD_TO_TYPE(le32_to_cpu(cmd_trb->generic.field[3]));
switch (cmd_type) {
case TRB_ENABLE_SLOT:
- xhci_handle_cmd_enable_slot(xhci, slot_id, cmd, cmd_comp_code);
+ xhci_handle_cmd_enable_slot(slot_id, cmd, cmd_comp_code);
break;
case TRB_DISABLE_SLOT:
xhci_handle_cmd_disable_slot(xhci, slot_id);
break;
case TRB_CONFIG_EP:
if (!cmd->completion)
- xhci_handle_cmd_config_ep(xhci, slot_id, cmd_comp_code);
+ xhci_handle_cmd_config_ep(xhci, slot_id);
break;
case TRB_EVAL_CONTEXT:
break;
@@ -1905,9 +1877,7 @@ static void xhci_cavium_reset_phy_quirk(struct xhci_hcd *xhci)
} while (!(pll_lock_check & 0x1) && --retry_count);
}
-static void handle_port_status(struct xhci_hcd *xhci,
- struct xhci_interrupter *ir,
- union xhci_trb *event)
+static void handle_port_status(struct xhci_hcd *xhci, union xhci_trb *event)
{
struct usb_hcd *hcd;
u32 port_id;
@@ -2156,30 +2126,34 @@ static void xhci_clear_hub_tt_buffer(struct xhci_hcd *xhci, struct xhci_td *td,
}
}
-/* Check if an error has halted the endpoint ring. The class driver will
- * cleanup the halt for a non-default control endpoint if we indicate a stall.
- * However, a babble and other errors also halt the endpoint ring, and the class
- * driver won't clear the halt in that case, so we need to issue a Set Transfer
- * Ring Dequeue Pointer command manually.
+/*
+ * Check if xhci internal endpoint state has gone to a "halt" state due to an
+ * error or stall, including default control pipe protocol stall.
+ * The internal halt needs to be cleared with a reset endpoint command.
+ *
+ * External device side is also halted in functional stall cases. Class driver
+ * will clear the device halt with a CLEAR_FEATURE(ENDPOINT_HALT) request later.
*/
-static int xhci_requires_manual_halt_cleanup(struct xhci_hcd *xhci,
- struct xhci_ep_ctx *ep_ctx,
- unsigned int trb_comp_code)
+static bool xhci_halted_host_endpoint(struct xhci_ep_ctx *ep_ctx, unsigned int comp_code)
{
- /* TRB completion codes that may require a manual halt cleanup */
- if (trb_comp_code == COMP_USB_TRANSACTION_ERROR ||
- trb_comp_code == COMP_BABBLE_DETECTED_ERROR ||
- trb_comp_code == COMP_SPLIT_TRANSACTION_ERROR)
- /* The 0.95 spec says a babbling control endpoint
- * is not halted. The 0.96 spec says it is. Some HW
- * claims to be 0.95 compliant, but it halts the control
- * endpoint anyway. Check if a babble halted the
- * endpoint.
+ /* Stall halts both internal and device side endpoint */
+ if (comp_code == COMP_STALL_ERROR)
+ return true;
+
+ /* TRB completion codes that may require internal halt cleanup */
+ if (comp_code == COMP_USB_TRANSACTION_ERROR ||
+ comp_code == COMP_BABBLE_DETECTED_ERROR ||
+ comp_code == COMP_SPLIT_TRANSACTION_ERROR)
+ /*
+ * The 0.95 spec says a babbling control endpoint is not halted.
+ * The 0.96 spec says it is. Some HW claims to be 0.95
+ * compliant, but it halts the control endpoint anyway.
+ * Check endpoint context if endpoint is halted.
*/
if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_HALTED)
- return 1;
+ return true;
- return 0;
+ return false;
}
int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code)
@@ -2349,8 +2323,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
case COMP_STOPPED_LENGTH_INVALID:
goto finish_td;
default:
- if (!xhci_requires_manual_halt_cleanup(xhci,
- ep_ctx, trb_comp_code))
+ if (!xhci_halted_host_endpoint(ep_ctx, trb_comp_code))
break;
xhci_dbg(xhci, "TRB error %u, halted endpoint index = %u\n",
trb_comp_code, ep->ep_index);
@@ -2460,7 +2433,9 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
requested = remaining;
break;
case COMP_STOPPED_LENGTH_INVALID:
- requested = 0;
+ /* exclude stopped trb with invalid length from length sum */
+ sum_trbs_for_length = true;
+ ep_trb_len = 0;
remaining = 0;
break;
default:
@@ -2589,6 +2564,33 @@ finish_td:
return finish_td(xhci, ep, ep_ring, td, trb_comp_code);
}
+/* Transfer events which don't point to a transfer TRB, see xhci 4.17.4 */
+static int handle_transferless_tx_event(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
+ u32 trb_comp_code)
+{
+ switch (trb_comp_code) {
+ case COMP_STALL_ERROR:
+ case COMP_USB_TRANSACTION_ERROR:
+ case COMP_INVALID_STREAM_TYPE_ERROR:
+ case COMP_INVALID_STREAM_ID_ERROR:
+ xhci_dbg(xhci, "Stream transaction error ep %u no id\n", ep->ep_index);
+ if (ep->err_count++ > MAX_SOFT_RETRY)
+ xhci_handle_halted_endpoint(xhci, ep, NULL, EP_HARD_RESET);
+ else
+ xhci_handle_halted_endpoint(xhci, ep, NULL, EP_SOFT_RESET);
+ break;
+ case COMP_RING_UNDERRUN:
+ case COMP_RING_OVERRUN:
+ case COMP_STOPPED_LENGTH_INVALID:
+ break;
+ default:
+ xhci_err(xhci, "Transfer event %u for unknown stream ring slot %u ep %u\n",
+ trb_comp_code, ep->vdev->slot_id, ep->ep_index);
+ return -ENODEV;
+ }
+ return 0;
+}
+
/*
* If this function returns an error condition, it means it got a Transfer
* event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
@@ -2609,7 +2611,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
int status = -EINPROGRESS;
struct xhci_ep_ctx *ep_ctx;
u32 trb_comp_code;
- int td_num = 0;
slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
@@ -2632,37 +2633,8 @@ static int handle_tx_event(struct xhci_hcd *xhci,
goto err_out;
}
- /* Some transfer events don't always point to a trb, see xhci 4.17.4 */
- if (!ep_ring) {
- switch (trb_comp_code) {
- case COMP_STALL_ERROR:
- case COMP_USB_TRANSACTION_ERROR:
- case COMP_INVALID_STREAM_TYPE_ERROR:
- case COMP_INVALID_STREAM_ID_ERROR:
- xhci_dbg(xhci, "Stream transaction error ep %u no id\n",
- ep_index);
- if (ep->err_count++ > MAX_SOFT_RETRY)
- xhci_handle_halted_endpoint(xhci, ep, NULL,
- EP_HARD_RESET);
- else
- xhci_handle_halted_endpoint(xhci, ep, NULL,
- EP_SOFT_RESET);
- break;
- case COMP_RING_UNDERRUN:
- case COMP_RING_OVERRUN:
- case COMP_STOPPED_LENGTH_INVALID:
- break;
- default:
- xhci_err(xhci, "ERROR Transfer event for unknown stream ring slot %u ep %u\n",
- slot_id, ep_index);
- goto err_out;
- }
- return 0;
- }
-
- /* Count current td numbers if ep->skip is set */
- if (ep->skip)
- td_num += list_count_nodes(&ep_ring->td_list);
+ if (!ep_ring)
+ return handle_transferless_tx_event(xhci, ep, trb_comp_code);
/* Look for common error cases */
switch (trb_comp_code) {
@@ -2744,18 +2716,12 @@ static int handle_tx_event(struct xhci_hcd *xhci,
* a Ring Overrun Event for IN Isoch endpoint or Ring
* Underrun Event for OUT Isoch endpoint.
*/
- xhci_dbg(xhci, "underrun event on endpoint\n");
- if (!list_empty(&ep_ring->td_list))
- xhci_dbg(xhci, "Underrun Event for slot %u ep %d still with TDs queued?\n",
- slot_id, ep_index);
+ xhci_dbg(xhci, "Underrun event on slot %u ep %u\n", slot_id, ep_index);
if (ep->skip)
break;
return 0;
case COMP_RING_OVERRUN:
- xhci_dbg(xhci, "overrun event on endpoint\n");
- if (!list_empty(&ep_ring->td_list))
- xhci_dbg(xhci, "Overrun Event for slot %u ep %d still with TDs queued?\n",
- slot_id, ep_index);
+ xhci_dbg(xhci, "Overrun event on slot %u ep %u\n", slot_id, ep_index);
if (ep->skip)
break;
return 0;
@@ -2822,44 +2788,17 @@ static int handle_tx_event(struct xhci_hcd *xhci,
xhci_dbg(xhci, "td_list is empty while skip flag set. Clear skip flag for slot %u ep %u.\n",
slot_id, ep_index);
}
- if (trb_comp_code == COMP_STALL_ERROR ||
- xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
- trb_comp_code)) {
- xhci_handle_halted_endpoint(xhci, ep, NULL,
- EP_HARD_RESET);
- }
- return 0;
- }
- /* We've skipped all the TDs on the ep ring when ep->skip set */
- if (ep->skip && td_num == 0) {
- ep->skip = false;
- xhci_dbg(xhci, "All tds on the ep_ring skipped. Clear skip flag for slot %u ep %u.\n",
- slot_id, ep_index);
- return 0;
+ td = NULL;
+ goto check_endpoint_halted;
}
td = list_first_entry(&ep_ring->td_list, struct xhci_td,
td_list);
- if (ep->skip)
- td_num--;
/* Is this a TRB in the currently executing TD? */
ep_seg = trb_in_td(xhci, td, ep_trb_dma, false);
- /*
- * Skip the Force Stopped Event. The event_trb(event_dma) of FSE
- * is not in the current TD pointed by ep_ring->dequeue because
- * that the hardware dequeue pointer still at the previous TRB
- * of the current TD. The previous TRB maybe a Link TD or the
- * last TRB of the previous TD. The command completion handle
- * will take care the rest.
- */
- if (!ep_seg && (trb_comp_code == COMP_STOPPED ||
- trb_comp_code == COMP_STOPPED_LENGTH_INVALID)) {
- continue;
- }
-
if (!ep_seg) {
if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
@@ -2868,6 +2807,18 @@ static int handle_tx_event(struct xhci_hcd *xhci,
}
/*
+ * Skip the Force Stopped Event. The 'ep_trb' of FSE is not in the current
+ * TD pointed by 'ep_ring->dequeue' because that the hardware dequeue
+ * pointer still at the previous TRB of the current TD. The previous TRB
+ * maybe a Link TD or the last TRB of the previous TD. The command
+ * completion handle will take care the rest.
+ */
+ if (trb_comp_code == COMP_STOPPED ||
+ trb_comp_code == COMP_STOPPED_LENGTH_INVALID) {
+ return 0;
+ }
+
+ /*
* Some hosts give a spurious success event after a short
* transfer. Ignore it.
*/
@@ -2916,10 +2867,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
return -ESHUTDOWN;
}
}
- if (trb_comp_code == COMP_SHORT_PACKET)
- ep_ring->last_td_was_short = true;
- else
- ep_ring->last_td_was_short = false;
if (ep->skip) {
xhci_dbg(xhci,
@@ -2928,37 +2875,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
ep->skip = false;
}
- ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) /
- sizeof(*ep_trb)];
-
- trace_xhci_handle_transfer(ep_ring,
- (struct xhci_generic_trb *) ep_trb);
-
- /*
- * No-op TRB could trigger interrupts in a case where
- * a URB was killed and a STALL_ERROR happens right
- * after the endpoint ring stopped. Reset the halted
- * endpoint. Otherwise, the endpoint remains stalled
- * indefinitely.
- */
-
- if (trb_is_noop(ep_trb)) {
- if (trb_comp_code == COMP_STALL_ERROR ||
- xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
- trb_comp_code))
- xhci_handle_halted_endpoint(xhci, ep, td,
- EP_HARD_RESET);
- } else {
- td->status = status;
-
- /* update the urb's actual_length and give back to the core */
- if (usb_endpoint_xfer_control(&td->urb->ep->desc))
- process_ctrl_td(xhci, ep, ep_ring, td, ep_trb, event);
- else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))
- process_isoc_td(xhci, ep, ep_ring, td, ep_trb, event);
- else
- process_bulk_intr_td(xhci, ep, ep_ring, td, ep_trb, event);
- }
/*
* If ep->skip is set, it means there are missed tds on the
* endpoint ring need to take care of.
@@ -2967,6 +2883,38 @@ static int handle_tx_event(struct xhci_hcd *xhci,
*/
} while (ep->skip);
+ if (trb_comp_code == COMP_SHORT_PACKET)
+ ep_ring->last_td_was_short = true;
+ else
+ ep_ring->last_td_was_short = false;
+
+ ep_trb = &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) / sizeof(*ep_trb)];
+ trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb);
+
+ /*
+ * No-op TRB could trigger interrupts in a case where a URB was killed
+ * and a STALL_ERROR happens right after the endpoint ring stopped.
+ * Reset the halted endpoint. Otherwise, the endpoint remains stalled
+ * indefinitely.
+ */
+
+ if (trb_is_noop(ep_trb))
+ goto check_endpoint_halted;
+
+ td->status = status;
+
+ /* update the urb's actual_length and give back to the core */
+ if (usb_endpoint_xfer_control(&td->urb->ep->desc))
+ process_ctrl_td(xhci, ep, ep_ring, td, ep_trb, event);
+ else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))
+ process_isoc_td(xhci, ep, ep_ring, td, ep_trb, event);
+ else
+ process_bulk_intr_td(xhci, ep, ep_ring, td, ep_trb, event);
+
+check_endpoint_halted:
+ if (xhci_halted_host_endpoint(ep_ctx, trb_comp_code))
+ xhci_handle_halted_endpoint(xhci, ep, td, EP_HARD_RESET);
+
return 0;
err_out:
@@ -3005,7 +2953,7 @@ static int xhci_handle_event_trb(struct xhci_hcd *xhci, struct xhci_interrupter
handle_cmd_completion(xhci, &event->event_cmd);
break;
case TRB_PORT_STATUS:
- handle_port_status(xhci, ir, event);
+ handle_port_status(xhci, event);
break;
case TRB_TRANSFER:
handle_tx_event(xhci, ir, &event->trans_event);
@@ -3065,8 +3013,7 @@ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
}
/* Clear the interrupt pending bit for a specific interrupter. */
-static void xhci_clear_interrupt_pending(struct xhci_hcd *xhci,
- struct xhci_interrupter *ir)
+static void xhci_clear_interrupt_pending(struct xhci_interrupter *ir)
{
if (!ir->ip_autoclear) {
u32 irq_pending;
@@ -3087,7 +3034,7 @@ static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir
int err;
u64 temp;
- xhci_clear_interrupt_pending(xhci, ir);
+ xhci_clear_interrupt_pending(ir);
/* Event ring hasn't been allocated yet. */
if (!ir->event_ring || !ir->event_ring->dequeue) {
@@ -3260,7 +3207,7 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
if (ep_ring != xhci->cmd_ring) {
new_segs = xhci_ring_expansion_needed(xhci, ep_ring, num_trbs);
- } else if (xhci_num_trbs_free(xhci, ep_ring) <= num_trbs) {
+ } else if (xhci_num_trbs_free(ep_ring) <= num_trbs) {
xhci_err(xhci, "Do not support expand command ring\n");
return -ENOMEM;
}
@@ -3278,9 +3225,7 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
/* If we're not dealing with 0.95 hardware or isoc rings
* on AMD 0.96 host, clear the chain bit.
*/
- if (!xhci_link_trb_quirk(xhci) &&
- !(ep_ring->type == TYPE_ISOC &&
- (xhci->quirks & XHCI_AMD_0x96_HOST)))
+ if (!xhci_link_chain_quirk(xhci, ep_ring->type))
ep_ring->enqueue->link.control &=
cpu_to_le32(~TRB_CHAIN);
else
@@ -3435,8 +3380,7 @@ static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id,
xhci_ring_ep_doorbell(xhci, slot_id, ep_index, stream_id);
}
-static void check_interval(struct xhci_hcd *xhci, struct urb *urb,
- struct xhci_ep_ctx *ep_ctx)
+static void check_interval(struct urb *urb, struct xhci_ep_ctx *ep_ctx)
{
int xhci_interval;
int ep_interval;
@@ -3477,7 +3421,7 @@ int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
struct xhci_ep_ctx *ep_ctx;
ep_ctx = xhci_get_ep_ctx(xhci, xhci->devs[slot_id]->out_ctx, ep_index);
- check_interval(xhci, urb, ep_ctx);
+ check_interval(urb, ep_ctx);
return xhci_queue_bulk_tx(xhci, mem_flags, urb, slot_id, ep_index);
}
@@ -3723,7 +3667,6 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
upper_32_bits(send_addr),
length_field,
field);
- td->num_trbs++;
addr += trb_buff_len;
sent_len = trb_buff_len;
@@ -3750,7 +3693,6 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
urb_priv->td[1].last_trb_seg = ring->enq_seg;
field = TRB_TYPE(TRB_NORMAL) | ring->cycle_state | TRB_IOC;
queue_trb(xhci, ring, 0, 0, 0, TRB_INTR_TARGET(0), field);
- urb_priv->td[1].num_trbs++;
}
check_trb_math(urb, enqd_len);
@@ -3801,7 +3743,6 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
urb_priv = urb->hcpriv;
td = &urb_priv->td[0];
- td->num_trbs = num_trbs;
/*
* Don't give the first TRB to the hardware (by toggling the cycle bit)
@@ -4122,7 +4063,6 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
goto cleanup;
}
td = &urb_priv->td[i];
- td->num_trbs = trbs_per_td;
/* use SIA as default, if frame id is used overwrite it */
sia_frame_id = TRB_SIA;
if (!(urb->transfer_flags & URB_ISO_ASAP) &&
@@ -4286,7 +4226,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags,
* Check interval value. This should be done before we start to
* calculate the start frame value.
*/
- check_interval(xhci, urb, ep_ctx);
+ check_interval(urb, ep_ctx);
/* Calculate the start frame and put it in urb->start_frame. */
if (HCC_CFC(xhci->hcc_params) && !list_empty(&ep_ring->td_list)) {
@@ -4439,7 +4379,7 @@ int xhci_queue_stop_endpoint(struct xhci_hcd *xhci, struct xhci_command *cmd,
int slot_id, unsigned int ep_index, int suspend)
{
u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
- u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
+ u32 trb_ep_index = EP_INDEX_FOR_TRB(ep_index);
u32 type = TRB_TYPE(TRB_STOP_RING);
u32 trb_suspend = SUSPEND_PORT_FOR_TRB(suspend);
@@ -4452,7 +4392,7 @@ int xhci_queue_reset_ep(struct xhci_hcd *xhci, struct xhci_command *cmd,
enum xhci_ep_reset_type reset_type)
{
u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
- u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
+ u32 trb_ep_index = EP_INDEX_FOR_TRB(ep_index);
u32 type = TRB_TYPE(TRB_RESET_EP);
if (reset_type == EP_SOFT_RESET)
diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h
index 5762564b9d73..24405315ffe6 100644
--- a/drivers/usb/host/xhci-trace.h
+++ b/drivers/usb/host/xhci-trace.h
@@ -250,6 +250,7 @@ DECLARE_EVENT_CLASS(xhci_log_urb,
TP_PROTO(struct urb *urb),
TP_ARGS(urb),
TP_STRUCT__entry(
+ __string(devname, dev_name(&urb->dev->dev))
__field(void *, urb)
__field(unsigned int, pipe)
__field(unsigned int, stream)
@@ -265,6 +266,7 @@ DECLARE_EVENT_CLASS(xhci_log_urb,
__field(int, slot_id)
),
TP_fast_assign(
+ __assign_str(devname);
__entry->urb = urb;
__entry->pipe = urb->pipe;
__entry->stream = urb->stream_id;
@@ -279,7 +281,8 @@ DECLARE_EVENT_CLASS(xhci_log_urb,
__entry->type = usb_endpoint_type(&urb->ep->desc);
__entry->slot_id = urb->dev->slot_id;
),
- TP_printk("ep%d%s-%s: urb %p pipe %u slot %d length %d/%d sgs %d/%d stream %d flags %08x",
+ TP_printk("%s ep%d%s-%s: urb %p pipe %u slot %d length %d/%d sgs %d/%d stream %d flags %08x",
+ __get_str(devname),
__entry->epnum, __entry->dir_in ? "in" : "out",
__print_symbolic(__entry->type,
{ USB_ENDPOINT_XFER_INT, "intr" },
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 78d014c4d884..ebd0afd59a60 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -17,6 +17,7 @@
#include <linux/kernel.h>
#include <linux/usb/hcd.h>
#include <linux/io-64-nonatomic-lo-hi.h>
+#include <linux/io-64-nonatomic-hi-lo.h>
/* Code sharing between pci-quirks and xhci hcd */
#include "xhci-ext-caps.h"
@@ -805,13 +806,19 @@ struct xhci_transfer_event {
__le32 flags;
};
+/* Transfer event flags bitfield, also for select command completion events */
+#define TRB_TO_SLOT_ID(p) (((p) >> 24) & 0xff)
+#define SLOT_ID_FOR_TRB(p) (((p) & 0xff) << 24)
+
+#define TRB_TO_EP_ID(p) (((p) >> 16) & 0x1f) /* Endpoint ID 1 - 31 */
+#define EP_ID_FOR_TRB(p) (((p) & 0x1f) << 16)
+
+#define TRB_TO_EP_INDEX(p) (TRB_TO_EP_ID(p) - 1) /* Endpoint index 0 - 30 */
+#define EP_INDEX_FOR_TRB(p) ((((p) + 1) & 0x1f) << 16)
+
/* Transfer event TRB length bit mask */
-/* bits 0:23 */
#define EVENT_TRB_LEN(p) ((p) & 0xffffff)
-/** Transfer Event bit fields **/
-#define TRB_TO_EP_ID(p) (((p) >> 16) & 0x1f)
-
/* Completion Code - only applicable for some types of TRBs */
#define COMP_CODE_MASK (0xff << 24)
#define GET_COMP_CODE(p) (((p) & COMP_CODE_MASK) >> 24)
@@ -950,8 +957,6 @@ struct xhci_event_cmd {
__le32 flags;
};
-/* flags bitmasks */
-
/* Address device - disable SetAddress */
#define TRB_BSR (1<<9)
@@ -987,13 +992,8 @@ enum xhci_setup_dev {
/* bits 16:23 are the virtual function ID */
/* bits 24:31 are the slot ID */
-#define TRB_TO_SLOT_ID(p) (((p) & (0xff<<24)) >> 24)
-#define SLOT_ID_FOR_TRB(p) (((p) & 0xff) << 24)
/* Stop Endpoint TRB - ep_index to endpoint ID for this TRB */
-#define TRB_TO_EP_INDEX(p) ((((p) & (0x1f << 16)) >> 16) - 1)
-#define EP_ID_FOR_TRB(p) ((((p) + 1) & 0x1f) << 16)
-
#define SUSPEND_PORT_FOR_TRB(p) (((p) & 1) << 23)
#define TRB_TO_SUSPEND_PORT(p) (((p) & (1 << 23)) >> 23)
#define LAST_EP_INDEX 30
@@ -1294,7 +1294,6 @@ struct xhci_td {
/* actual_length of the URB has already been set */
bool urb_length_set;
bool error_mid_td;
- unsigned int num_trbs;
};
/*
@@ -1628,6 +1627,7 @@ struct xhci_hcd {
#define XHCI_RESET_TO_DEFAULT BIT_ULL(44)
#define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(45)
#define XHCI_ZHAOXIN_HOST BIT_ULL(46)
+#define XHCI_WRITE_64_HI_LO BIT_ULL(47)
unsigned int num_active_eps;
unsigned int limit_active_eps;
@@ -1749,9 +1749,12 @@ static inline void xhci_write_64(struct xhci_hcd *xhci,
lo_hi_writeq(val, regs);
}
-static inline int xhci_link_trb_quirk(struct xhci_hcd *xhci)
+
+/* Link TRB chain should always be set on 0.95 hosts, and AMD 0.96 ISOC rings */
+static inline bool xhci_link_chain_quirk(struct xhci_hcd *xhci, enum xhci_ring_type type)
{
- return xhci->quirks & XHCI_LINK_TRB_QUIRK;
+ return (xhci->quirks & XHCI_LINK_TRB_QUIRK) ||
+ (type == TYPE_ISOC && (xhci->quirks & XHCI_AMD_0x96_HOST));
}
/* xHCI debugging */
@@ -2021,8 +2024,7 @@ static inline const char *xhci_decode_trb(char *str, size_t size,
field1, field0,
xhci_trb_comp_code_string(GET_COMP_CODE(field2)),
EVENT_TRB_LEN(field2), TRB_TO_SLOT_ID(field3),
- /* Macro decrements 1, maybe it shouldn't?!? */
- TRB_TO_EP_INDEX(field3) + 1,
+ TRB_TO_EP_ID(field3),
xhci_trb_type_string(type),
field3 & EVENT_DATA ? 'E' : 'e',
field3 & TRB_CYCLE ? 'C' : 'c');
@@ -2137,8 +2139,7 @@ static inline const char *xhci_decode_trb(char *str, size_t size,
xhci_trb_type_string(type),
field1, field0,
TRB_TO_SLOT_ID(field3),
- /* Macro decrements 1, maybe it shouldn't?!? */
- TRB_TO_EP_INDEX(field3) + 1,
+ TRB_TO_EP_ID(field3),
field3 & TRB_TSP ? 'T' : 't',
field3 & TRB_CYCLE ? 'C' : 'c');
break;
@@ -2148,8 +2149,7 @@ static inline const char *xhci_decode_trb(char *str, size_t size,
xhci_trb_type_string(type),
TRB_TO_SLOT_ID(field3),
TRB_TO_SUSPEND_PORT(field3),
- /* Macro decrements 1, maybe it shouldn't?!? */
- TRB_TO_EP_INDEX(field3) + 1,
+ TRB_TO_EP_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_SET_DEQ:
@@ -2159,8 +2159,7 @@ static inline const char *xhci_decode_trb(char *str, size_t size,
field1, field0,
TRB_TO_STREAM_ID(field2),
TRB_TO_SLOT_ID(field3),
- /* Macro decrements 1, maybe it shouldn't?!? */
- TRB_TO_EP_INDEX(field3) + 1,
+ TRB_TO_EP_ID(field3),
field3 & TRB_CYCLE ? 'C' : 'c');
break;
case TRB_RESET_DEV:
diff --git a/drivers/usb/misc/ezusb.c b/drivers/usb/misc/ezusb.c
index 78aaee56c2b7..7ab4856aba0e 100644
--- a/drivers/usb/misc/ezusb.c
+++ b/drivers/usb/misc/ezusb.c
@@ -148,4 +148,5 @@ int ezusb_fx2_ihex_firmware_download(struct usb_device *dev,
EXPORT_SYMBOL_GPL(ezusb_fx2_ihex_firmware_download);
#endif
+MODULE_DESCRIPTION("EZUSB device support");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/isight_firmware.c b/drivers/usb/misc/isight_firmware.c
index 4d30095d6ad2..2d9af74a62c1 100644
--- a/drivers/usb/misc/isight_firmware.c
+++ b/drivers/usb/misc/isight_firmware.c
@@ -127,5 +127,6 @@ static struct usb_driver isight_firmware_driver = {
module_usb_driver(isight_firmware_driver);
+MODULE_DESCRIPTION("iSight firmware loading support");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Matthew Garrett <mjg@redhat.com>");
diff --git a/drivers/usb/misc/onboard_usb_dev.c b/drivers/usb/misc/onboard_usb_dev.c
index f2bcc1a8b95f..56710e6b1653 100644
--- a/drivers/usb/misc/onboard_usb_dev.c
+++ b/drivers/usb/misc/onboard_usb_dev.c
@@ -454,16 +454,18 @@ static struct onboard_dev *_find_onboard_dev(struct device *dev)
return onboard_dev;
}
+static bool onboard_dev_usbdev_match(struct usb_device *udev)
+{
+ /* Onboard devices using this driver must have a device tree node */
+ return !!udev->dev.of_node;
+}
+
static int onboard_dev_usbdev_probe(struct usb_device *udev)
{
struct device *dev = &udev->dev;
struct onboard_dev *onboard_dev;
int err;
- /* ignore supported devices without device tree node */
- if (!dev->of_node)
- return -ENODEV;
-
onboard_dev = _find_onboard_dev(dev);
if (IS_ERR(onboard_dev))
return PTR_ERR(onboard_dev);
@@ -513,6 +515,7 @@ MODULE_DEVICE_TABLE(usb, onboard_dev_id_table);
static struct usb_device_driver onboard_dev_usbdev_driver = {
.name = "onboard-usb-dev",
+ .match = onboard_dev_usbdev_match,
.probe = onboard_dev_usbdev_probe,
.disconnect = onboard_dev_usbdev_disconnect,
.generic_subclass = 1,
diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c
index 7da404f55a6d..b98cda1cef73 100644
--- a/drivers/usb/misc/usb251xb.c
+++ b/drivers/usb/misc/usb251xb.c
@@ -726,15 +726,15 @@ static int __maybe_unused usb251xb_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(usb251xb_pm_ops, usb251xb_suspend, usb251xb_resume);
static const struct i2c_device_id usb251xb_id[] = {
- { "usb2422", 0 },
- { "usb2512b", 0 },
- { "usb2512bi", 0 },
- { "usb2513b", 0 },
- { "usb2513bi", 0 },
- { "usb2514b", 0 },
- { "usb2514bi", 0 },
- { "usb2517", 0 },
- { "usb2517i", 0 },
+ { "usb2422" },
+ { "usb2512b" },
+ { "usb2512bi" },
+ { "usb2513b" },
+ { "usb2513bi" },
+ { "usb2514b" },
+ { "usb2514bi" },
+ { "usb2517" },
+ { "usb2517i" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, usb251xb_id);
diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c
index 72765077932c..3b33e4878c60 100644
--- a/drivers/usb/misc/usb3503.c
+++ b/drivers/usb/misc/usb3503.c
@@ -390,7 +390,7 @@ static SIMPLE_DEV_PM_OPS(usb3503_platform_pm_ops, usb3503_platform_suspend,
usb3503_platform_resume);
static const struct i2c_device_id usb3503_id[] = {
- { USB3503_I2C_NAME, 0 },
+ { USB3503_I2C_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, usb3503_id);
diff --git a/drivers/usb/misc/usb4604.c b/drivers/usb/misc/usb4604.c
index 065e269ba4e3..c9a2fb3518ae 100644
--- a/drivers/usb/misc/usb4604.c
+++ b/drivers/usb/misc/usb4604.c
@@ -135,7 +135,7 @@ static SIMPLE_DEV_PM_OPS(usb4604_i2c_pm_ops, usb4604_i2c_suspend,
usb4604_i2c_resume);
static const struct i2c_device_id usb4604_id[] = {
- { "usb4604", 0 },
+ { "usb4604" },
{ }
};
MODULE_DEVICE_TABLE(i2c, usb4604_id);
diff --git a/drivers/usb/misc/yurex.c b/drivers/usb/misc/yurex.c
index 9a0649d23693..4745a320eae4 100644
--- a/drivers/usb/misc/yurex.c
+++ b/drivers/usb/misc/yurex.c
@@ -531,4 +531,5 @@ static const struct file_operations yurex_fops = {
module_usb_driver(yurex_driver);
+MODULE_DESCRIPTION("USB YUREX driver support");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c
index 824904abe76f..af852d53aac6 100644
--- a/drivers/usb/mon/mon_main.c
+++ b/drivers/usb/mon/mon_main.c
@@ -419,4 +419,5 @@ static void __exit mon_exit(void)
module_init(mon_init);
module_exit(mon_exit);
+MODULE_DESCRIPTION("USB Monitor");
MODULE_LICENSE("GPL");
diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c
index 108d9a593a80..953094c1930c 100644
--- a/drivers/usb/musb/da8xx.c
+++ b/drivers/usb/musb/da8xx.c
@@ -191,7 +191,7 @@ static void otg_timer(struct timer_list *t)
spin_unlock_irqrestore(&musb->lock, flags);
}
-static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout)
+static void __maybe_unused da8xx_musb_try_idle(struct musb *musb, unsigned long timeout)
{
static unsigned long last_timer;
@@ -220,6 +220,13 @@ static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout)
mod_timer(&musb->dev_timer, timeout);
}
+static int da8xx_babble_recover(struct musb *musb)
+{
+ dev_dbg(musb->controller, "resetting controller to recover from babble\n");
+ musb_writel(musb->ctrl_base, DA8XX_USB_CTRL_REG, DA8XX_SOFT_RESET_MASK);
+ return 0;
+}
+
static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)
{
struct musb *musb = hci;
@@ -328,13 +335,6 @@ static int da8xx_musb_set_mode(struct musb *musb, u8 musb_mode)
struct da8xx_glue *glue = dev_get_drvdata(musb->controller->parent);
enum phy_mode phy_mode;
- /*
- * The PHY has some issues when it is forced in device or host mode.
- * Unless the user request another mode, configure the PHY in OTG mode.
- */
- if (!musb->is_initialized)
- return phy_set_mode(glue->phy, PHY_MODE_USB_OTG);
-
switch (musb_mode) {
case MUSB_HOST: /* Force VBUS valid, ID = 0 */
phy_mode = PHY_MODE_USB_HOST;
@@ -483,7 +483,11 @@ static const struct musb_platform_ops da8xx_ops = {
.disable = da8xx_musb_disable,
.set_mode = da8xx_musb_set_mode,
+
+#ifndef CONFIG_USB_MUSB_HOST
.try_idle = da8xx_musb_try_idle,
+#endif
+ .recover = da8xx_babble_recover,
.set_vbus = da8xx_musb_set_vbus,
};
diff --git a/drivers/usb/musb/mpfs.c b/drivers/usb/musb/mpfs.c
index f0f56df38835..29c7e5cdb230 100644
--- a/drivers/usb/musb/mpfs.c
+++ b/drivers/usb/musb/mpfs.c
@@ -190,6 +190,8 @@ static int mpfs_probe(struct platform_device *pdev)
pdata->config = &mpfs_musb_hdrc_config;
pdata->platform_ops = &mpfs_ops;
+ pdata->extvbus = device_property_read_bool(dev, "microchip,ext-vbus-drv");
+
pdata->mode = usb_get_dr_mode(dev);
if (pdata->mode == USB_DR_MODE_UNKNOWN) {
dev_info(dev, "No dr_mode property found, defaulting to otg\n");
diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c
index 97e6603c7149..ada508be090a 100644
--- a/drivers/usb/phy/phy-am335x-control.c
+++ b/drivers/usb/phy/phy-am335x-control.c
@@ -189,4 +189,5 @@ static struct platform_driver am335x_control_driver = {
};
module_platform_driver(am335x_control_driver);
+MODULE_DESCRIPTION("AM335x USB PHY Control Driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c
index e39665cf4b4a..6db88e00f127 100644
--- a/drivers/usb/phy/phy-am335x.c
+++ b/drivers/usb/phy/phy-am335x.c
@@ -142,4 +142,5 @@ static struct platform_driver am335x_phy_driver = {
};
module_platform_driver(am335x_phy_driver);
+MODULE_DESCRIPTION("AM335x USB PHY Driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index b610a2de4ae5..c223b4dc1b19 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -423,6 +423,7 @@ static void uas_data_cmplt(struct urb *urb)
uas_log_cmd_state(cmnd, "data cmplt err", status);
/* error: no data transfered */
scsi_set_resid(cmnd, sdb->length);
+ set_host_byte(cmnd, DID_ERROR);
} else {
scsi_set_resid(cmnd, sdb->length - urb->actual_length);
}
@@ -1232,9 +1233,8 @@ static void uas_disconnect(struct usb_interface *intf)
* hang on reboot when the device is still in uas mode. Note the reset is
* necessary as some devices won't revert to usb-storage mode without it.
*/
-static void uas_shutdown(struct device *dev)
+static void uas_shutdown(struct usb_interface *intf)
{
- struct usb_interface *intf = to_usb_interface(dev);
struct usb_device *udev = interface_to_usbdev(intf);
struct Scsi_Host *shost = usb_get_intfdata(intf);
struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata;
@@ -1257,7 +1257,7 @@ static struct usb_driver uas_driver = {
.suspend = uas_suspend,
.resume = uas_resume,
.reset_resume = uas_reset_resume,
- .driver.shutdown = uas_shutdown,
+ .shutdown = uas_shutdown,
.id_table = uas_usb_ids,
};
@@ -1287,6 +1287,7 @@ static void __exit uas_exit(void)
module_init(uas_init);
module_exit(uas_exit);
+MODULE_DESCRIPTION("USB Attached SCSI driver");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS(USB_STORAGE);
MODULE_AUTHOR(
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index a49a31639f6f..d36f3b6992bb 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -68,9 +68,102 @@ MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
MODULE_DESCRIPTION("USB Mass Storage driver for Linux");
MODULE_LICENSE("GPL");
-static unsigned int delay_use = 1;
-module_param(delay_use, uint, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");
+static unsigned int delay_use = 1 * MSEC_PER_SEC;
+
+/**
+ * parse_delay_str - parse an unsigned decimal integer delay
+ * @str: String to parse.
+ * @ndecimals: Number of decimal to scale up.
+ * @suffix: Suffix string to parse.
+ * @val: Where to store the parsed value.
+ *
+ * Parse an unsigned decimal value in @str, optionally end with @suffix.
+ * Stores the parsed value in @val just as it is if @str ends with @suffix.
+ * Otherwise store the value scale up by 10^(@ndecimal).
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+static int parse_delay_str(const char *str, int ndecimals, const char *suffix,
+ unsigned int *val)
+{
+ int n, n2, l;
+ char buf[16];
+
+ l = strlen(suffix);
+ n = strlen(str);
+ if (n > 0 && str[n - 1] == '\n')
+ --n;
+ if (n >= l && !strncmp(&str[n - l], suffix, l)) {
+ n -= l;
+ n2 = 0;
+ } else
+ n2 = ndecimals;
+
+ if (n + n2 > sizeof(buf) - 1)
+ return -EINVAL;
+
+ memcpy(buf, str, n);
+ while (n2-- > 0)
+ buf[n++] = '0';
+ buf[n] = 0;
+
+ return kstrtouint(buf, 10, val);
+}
+
+/**
+ * format_delay_ms - format an integer value into a delay string
+ * @val: The integer value to format, scaled by 10^(@ndecimals).
+ * @ndecimals: Number of decimal to scale down.
+ * @suffix: Suffix string to format.
+ * @str: Where to store the formatted string.
+ * @size: The size of buffer for @str.
+ *
+ * Format an integer value in @val scale down by 10^(@ndecimals) without @suffix
+ * if @val is divisible by 10^(@ndecimals).
+ * Otherwise format a value in @val just as it is with @suffix
+ *
+ * Returns the number of characters written into @str.
+ */
+static int format_delay_ms(unsigned int val, int ndecimals, const char *suffix,
+ char *str, int size)
+{
+ u64 delay_ms = val;
+ unsigned int rem = do_div(delay_ms, int_pow(10, ndecimals));
+ int ret;
+
+ if (rem)
+ ret = scnprintf(str, size, "%u%s\n", val, suffix);
+ else
+ ret = scnprintf(str, size, "%u\n", (unsigned int)delay_ms);
+ return ret;
+}
+
+static int delay_use_set(const char *s, const struct kernel_param *kp)
+{
+ unsigned int delay_ms;
+ int ret;
+
+ ret = parse_delay_str(skip_spaces(s), 3, "ms", &delay_ms);
+ if (ret < 0)
+ return ret;
+
+ *((unsigned int *)kp->arg) = delay_ms;
+ return 0;
+}
+
+static int delay_use_get(char *s, const struct kernel_param *kp)
+{
+ unsigned int delay_ms = *((unsigned int *)kp->arg);
+
+ return format_delay_ms(delay_ms, 3, "ms", s, PAGE_SIZE);
+}
+
+static const struct kernel_param_ops delay_use_ops = {
+ .set = delay_use_set,
+ .get = delay_use_get,
+};
+module_param_cb(delay_use, &delay_use_ops, &delay_use, 0644);
+MODULE_PARM_DESC(delay_use, "time to delay before using a new device");
static char quirks[128];
module_param_string(quirks, quirks, sizeof(quirks), S_IRUGO | S_IWUSR);
@@ -1064,7 +1157,7 @@ int usb_stor_probe2(struct us_data *us)
if (delay_use > 0)
dev_dbg(dev, "waiting for device to settle before scanning\n");
queue_delayed_work(system_freezable_wq, &us->scan_dwork,
- delay_use * HZ);
+ msecs_to_jiffies(delay_use));
return 0;
/* We come here if there are any problems */
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
index 596cd4806018..92cc1b136120 100644
--- a/drivers/usb/typec/altmodes/displayport.c
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -746,7 +746,7 @@ int dp_altmode_probe(struct typec_altmode *alt)
dp->alt = alt;
alt->desc = "DisplayPort";
- alt->ops = &dp_altmode_ops;
+ typec_altmode_set_ops(alt, &dp_altmode_ops);
if (plug) {
plug->desc = "Displayport";
diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c
index b12a07edc71b..5a5bf3532ad7 100644
--- a/drivers/usb/typec/anx7411.c
+++ b/drivers/usb/typec/anx7411.c
@@ -1566,7 +1566,7 @@ static void anx7411_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id anx7411_id[] = {
- {"anx7411", 0},
+ { "anx7411" },
{}
};
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 9610e647a8d4..9262fcd4144f 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -467,6 +467,22 @@ static const struct attribute_group *typec_altmode_groups[] = {
NULL
};
+/**
+ * typec_altmode_set_ops - Set ops for altmode
+ * @adev: Handle to the alternate mode
+ * @ops: Ops for the alternate mode
+ *
+ * After setting ops, attribute visiblity needs to be refreshed if the alternate
+ * mode can be activated.
+ */
+void typec_altmode_set_ops(struct typec_altmode *adev,
+ const struct typec_altmode_ops *ops)
+{
+ adev->ops = ops;
+ sysfs_update_group(&adev->dev.kobj, &typec_altmode_group);
+}
+EXPORT_SYMBOL_GPL(typec_altmode_set_ops);
+
static int altmode_id_get(struct device *dev)
{
struct ida *ids;
@@ -2317,7 +2333,7 @@ void typec_port_register_altmodes(struct typec_port *port,
continue;
}
- alt->ops = ops;
+ typec_altmode_set_ops(alt, ops);
typec_altmode_set_drvdata(alt, drvdata);
altmodes[index] = alt;
index++;
diff --git a/drivers/usb/typec/mux/gpio-sbu-mux.c b/drivers/usb/typec/mux/gpio-sbu-mux.c
index 374168482d36..8902102c05a8 100644
--- a/drivers/usb/typec/mux/gpio-sbu-mux.c
+++ b/drivers/usb/typec/mux/gpio-sbu-mux.c
@@ -66,6 +66,9 @@ static int gpio_sbu_mux_set(struct typec_mux_dev *mux,
{
struct gpio_sbu_mux *sbu_mux = typec_mux_get_drvdata(mux);
+ if (!sbu_mux->enable_gpio)
+ return -EOPNOTSUPP;
+
mutex_lock(&sbu_mux->lock);
switch (state->mode) {
@@ -102,7 +105,8 @@ static int gpio_sbu_mux_probe(struct platform_device *pdev)
mutex_init(&sbu_mux->lock);
- sbu_mux->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+ sbu_mux->enable_gpio = devm_gpiod_get_optional(dev, "enable",
+ GPIOD_OUT_LOW);
if (IS_ERR(sbu_mux->enable_gpio))
return dev_err_probe(dev, PTR_ERR(sbu_mux->enable_gpio),
"unable to acquire enable gpio\n");
diff --git a/drivers/usb/typec/mux/nb7vpq904m.c b/drivers/usb/typec/mux/nb7vpq904m.c
index b17826713753..b57b6c9c40fe 100644
--- a/drivers/usb/typec/mux/nb7vpq904m.c
+++ b/drivers/usb/typec/mux/nb7vpq904m.c
@@ -69,6 +69,7 @@ struct nb7vpq904m {
bool swap_data_lanes;
struct typec_switch *typec_switch;
+ struct typec_mux *typec_mux;
struct mutex lock; /* protect non-concurrent retimer & switch */
@@ -275,6 +276,7 @@ static int nb7vpq904m_sw_set(struct typec_switch_dev *sw, enum typec_orientation
static int nb7vpq904m_retimer_set(struct typec_retimer *retimer, struct typec_retimer_state *state)
{
struct nb7vpq904m *nb7 = typec_retimer_get_drvdata(retimer);
+ struct typec_mux_state mux_state;
int ret = 0;
mutex_lock(&nb7->lock);
@@ -292,7 +294,14 @@ static int nb7vpq904m_retimer_set(struct typec_retimer *retimer, struct typec_re
mutex_unlock(&nb7->lock);
- return ret;
+ if (ret)
+ return ret;
+
+ mux_state.alt = state->alt;
+ mux_state.data = state->data;
+ mux_state.mode = state->mode;
+
+ return typec_mux_set(nb7->typec_mux, &mux_state);
}
static const struct regmap_config nb7_regmap = {
@@ -321,46 +330,48 @@ static int nb7vpq904m_parse_data_lanes_mapping(struct nb7vpq904m *nb7)
ep = of_graph_get_endpoint_by_regs(nb7->client->dev.of_node, 1, 0);
- if (ep) {
- ret = of_property_count_u32_elems(ep, "data-lanes");
- if (ret == -EINVAL)
- /* Property isn't here, consider default mapping */
- goto out_done;
- if (ret < 0)
- goto out_error;
-
- if (ret != DATA_LANES_COUNT) {
- dev_err(&nb7->client->dev, "expected 4 data lanes\n");
- ret = -EINVAL;
- goto out_error;
- }
+ if (!ep)
+ return 0;
+
+
+ ret = of_property_count_u32_elems(ep, "data-lanes");
+ if (ret == -EINVAL)
+ /* Property isn't here, consider default mapping */
+ goto out_done;
+ if (ret < 0)
+ goto out_error;
- ret = of_property_read_u32_array(ep, "data-lanes", data_lanes, DATA_LANES_COUNT);
- if (ret)
- goto out_error;
+ if (ret != DATA_LANES_COUNT) {
+ dev_err(&nb7->client->dev, "expected 4 data lanes\n");
+ ret = -EINVAL;
+ goto out_error;
+ }
- for (i = 0; i < ARRAY_SIZE(supported_data_lane_mapping); i++) {
- for (j = 0; j < DATA_LANES_COUNT; j++) {
- if (data_lanes[j] != supported_data_lane_mapping[i][j])
- break;
- }
+ ret = of_property_read_u32_array(ep, "data-lanes", data_lanes, DATA_LANES_COUNT);
+ if (ret)
+ goto out_error;
- if (j == DATA_LANES_COUNT)
+ for (i = 0; i < ARRAY_SIZE(supported_data_lane_mapping); i++) {
+ for (j = 0; j < DATA_LANES_COUNT; j++) {
+ if (data_lanes[j] != supported_data_lane_mapping[i][j])
break;
}
- switch (i) {
- case NORMAL_LANE_MAPPING:
- break;
- case INVERT_LANE_MAPPING:
- nb7->swap_data_lanes = true;
- dev_info(&nb7->client->dev, "using inverted data lanes mapping\n");
+ if (j == DATA_LANES_COUNT)
break;
- default:
- dev_err(&nb7->client->dev, "invalid data lanes mapping\n");
- ret = -EINVAL;
- goto out_error;
- }
+ }
+
+ switch (i) {
+ case NORMAL_LANE_MAPPING:
+ break;
+ case INVERT_LANE_MAPPING:
+ nb7->swap_data_lanes = true;
+ dev_info(&nb7->client->dev, "using inverted data lanes mapping\n");
+ break;
+ default:
+ dev_err(&nb7->client->dev, "invalid data lanes mapping\n");
+ ret = -EINVAL;
+ goto out_error;
}
out_done:
@@ -411,9 +422,16 @@ static int nb7vpq904m_probe(struct i2c_client *client)
return dev_err_probe(dev, PTR_ERR(nb7->typec_switch),
"failed to acquire orientation-switch\n");
+ nb7->typec_mux = fwnode_typec_mux_get(dev->fwnode);
+ if (IS_ERR(nb7->typec_mux)) {
+ ret = dev_err_probe(dev, PTR_ERR(nb7->typec_mux),
+ "Failed to acquire mode-switch\n");
+ goto err_switch_put;
+ }
+
ret = nb7vpq904m_parse_data_lanes_mapping(nb7);
if (ret)
- return ret;
+ goto err_mux_put;
ret = regulator_enable(nb7->vcc_supply);
if (ret)
@@ -456,6 +474,12 @@ err_disable_gpio:
gpiod_set_value(nb7->enable_gpio, 0);
regulator_disable(nb7->vcc_supply);
+err_mux_put:
+ typec_mux_put(nb7->typec_mux);
+
+err_switch_put:
+ typec_switch_put(nb7->typec_switch);
+
return ret;
}
@@ -469,6 +493,9 @@ static void nb7vpq904m_remove(struct i2c_client *client)
gpiod_set_value(nb7->enable_gpio, 0);
regulator_disable(nb7->vcc_supply);
+
+ typec_mux_put(nb7->typec_mux);
+ typec_switch_put(nb7->typec_switch);
}
static const struct i2c_device_id nb7vpq904m_table[] = {
diff --git a/drivers/usb/typec/mux/ptn36502.c b/drivers/usb/typec/mux/ptn36502.c
index 0ec86ef32a87..129d9d24b932 100644
--- a/drivers/usb/typec/mux/ptn36502.c
+++ b/drivers/usb/typec/mux/ptn36502.c
@@ -67,6 +67,7 @@ struct ptn36502 {
struct typec_retimer *retimer;
struct typec_switch *typec_switch;
+ struct typec_mux *typec_mux;
struct mutex lock; /* protect non-concurrent retimer & switch */
@@ -235,6 +236,7 @@ static int ptn36502_sw_set(struct typec_switch_dev *sw, enum typec_orientation o
static int ptn36502_retimer_set(struct typec_retimer *retimer, struct typec_retimer_state *state)
{
struct ptn36502 *ptn = typec_retimer_get_drvdata(retimer);
+ struct typec_mux_state mux_state;
int ret = 0;
mutex_lock(&ptn->lock);
@@ -252,7 +254,14 @@ static int ptn36502_retimer_set(struct typec_retimer *retimer, struct typec_reti
mutex_unlock(&ptn->lock);
- return ret;
+ if (ret)
+ return ret;
+
+ mux_state.alt = state->alt;
+ mux_state.data = state->data;
+ mux_state.mode = state->mode;
+
+ return typec_mux_set(ptn->typec_mux, &mux_state);
}
static int ptn36502_detect(struct ptn36502 *ptn)
@@ -321,9 +330,18 @@ static int ptn36502_probe(struct i2c_client *client)
return dev_err_probe(dev, PTR_ERR(ptn->typec_switch),
"Failed to acquire orientation-switch\n");
+ ptn->typec_mux = fwnode_typec_mux_get(dev->fwnode);
+ if (IS_ERR(ptn->typec_mux)) {
+ ret = dev_err_probe(dev, PTR_ERR(ptn->typec_mux),
+ "Failed to acquire mode-switch\n");
+ goto err_switch_put;
+ }
+
ret = regulator_enable(ptn->vdd18_supply);
- if (ret)
- return dev_err_probe(dev, ret, "Failed to enable vdd18\n");
+ if (ret) {
+ ret = dev_err_probe(dev, ret, "Failed to enable vdd18\n");
+ goto err_mux_put;
+ }
ret = ptn36502_detect(ptn);
if (ret)
@@ -363,6 +381,12 @@ err_switch_unregister:
err_disable_regulator:
regulator_disable(ptn->vdd18_supply);
+err_mux_put:
+ typec_mux_put(ptn->typec_mux);
+
+err_switch_put:
+ typec_switch_put(ptn->typec_switch);
+
return ret;
}
@@ -374,6 +398,9 @@ static void ptn36502_remove(struct i2c_client *client)
typec_switch_unregister(ptn->sw);
regulator_disable(ptn->vdd18_supply);
+
+ typec_mux_put(ptn->typec_mux);
+ typec_switch_put(ptn->typec_switch);
}
static const struct i2c_device_id ptn36502_table[] = {
diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c
index ef18a448b740..e2fe479e16ad 100644
--- a/drivers/usb/typec/tcpm/fusb302.c
+++ b/drivers/usb/typec/tcpm/fusb302.c
@@ -1820,8 +1820,8 @@ static const struct of_device_id fusb302_dt_match[] __maybe_unused = {
MODULE_DEVICE_TABLE(of, fusb302_dt_match);
static const struct i2c_device_id fusb302_i2c_device_id[] = {
- {"typec_fusb302", 0},
- {},
+ { "typec_fusb302" },
+ {}
};
MODULE_DEVICE_TABLE(i2c, fusb302_i2c_device_id);
diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index c962014bba4e..b862fdf3fe1d 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -67,6 +67,18 @@ static int tcpci_write16(struct tcpci *tcpci, unsigned int reg, u16 val)
return regmap_raw_write(tcpci->regmap, reg, &val, sizeof(u16));
}
+static bool tcpci_check_std_output_cap(struct regmap *regmap, u8 mask)
+{
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(regmap, TCPC_STD_OUTPUT_CAP, &reg);
+ if (ret < 0)
+ return ret;
+
+ return (reg & mask) == mask;
+}
+
static int tcpci_set_cc(struct tcpc_dev *tcpc, enum typec_cc_status cc)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
@@ -301,6 +313,28 @@ static int tcpci_set_polarity(struct tcpc_dev *tcpc,
TCPC_TCPC_CTRL_ORIENTATION : 0);
}
+static int tcpci_set_orientation(struct tcpc_dev *tcpc,
+ enum typec_orientation orientation)
+{
+ struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
+ unsigned int reg;
+
+ switch (orientation) {
+ case TYPEC_ORIENTATION_NONE:
+ /* We can't put a single output into high impedance */
+ fallthrough;
+ case TYPEC_ORIENTATION_NORMAL:
+ reg = TCPC_CONFIG_STD_OUTPUT_ORIENTATION_NORMAL;
+ break;
+ case TYPEC_ORIENTATION_REVERSE:
+ reg = TCPC_CONFIG_STD_OUTPUT_ORIENTATION_FLIPPED;
+ break;
+ }
+
+ return regmap_update_bits(tcpci->regmap, TCPC_CONFIG_STD_OUTPUT,
+ TCPC_CONFIG_STD_OUTPUT_ORIENTATION_MASK, reg);
+}
+
static void tcpci_set_partner_usb_comm_capable(struct tcpc_dev *tcpc, bool capable)
{
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
@@ -830,6 +864,9 @@ struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *data)
if (tcpci->data->vbus_vsafe0v)
tcpci->tcpc.is_vbus_vsafe0v = tcpci_is_vbus_vsafe0v;
+ if (tcpci->data->set_orientation)
+ tcpci->tcpc.set_orientation = tcpci_set_orientation;
+
err = tcpci_parse_config(tcpci);
if (err < 0)
return ERR_PTR(err);
@@ -873,6 +910,13 @@ static int tcpci_probe(struct i2c_client *client)
if (err < 0)
return err;
+ err = tcpci_check_std_output_cap(chip->data.regmap,
+ TCPC_STD_OUTPUT_CAP_ORIENTATION);
+ if (err < 0)
+ return err;
+
+ chip->data.set_orientation = err;
+
chip->tcpci = tcpci_register_port(&client->dev, &chip->data);
if (IS_ERR(chip->tcpci))
return PTR_ERR(chip->tcpci);
@@ -903,7 +947,7 @@ static void tcpci_remove(struct i2c_client *client)
}
static const struct i2c_device_id tcpci_id[] = {
- { "tcpci", 0 },
+ { "tcpci" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tcpci_id);
diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c
index eec3bcec119c..760e2f92b958 100644
--- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c
+++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c
@@ -538,7 +538,7 @@ static void max_tcpci_remove(struct i2c_client *client)
}
static const struct i2c_device_id max_tcpci_id[] = {
- { "maxtcpc", 0 },
+ { "maxtcpc" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max_tcpci_id);
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 5d4da962acc8..26f9006e95e1 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -57,6 +57,7 @@
S(SNK_DISCOVERY_DEBOUNCE), \
S(SNK_DISCOVERY_DEBOUNCE_DONE), \
S(SNK_WAIT_CAPABILITIES), \
+ S(SNK_WAIT_CAPABILITIES_TIMEOUT), \
S(SNK_NEGOTIATE_CAPABILITIES), \
S(SNK_NEGOTIATE_PPS_CAPABILITIES), \
S(SNK_TRANSITION_SINK), \
@@ -943,7 +944,7 @@ static int tcpm_pd_transmit(struct tcpm_port *port,
enum tcpm_transmit_type tx_sop_type,
const struct pd_message *msg)
{
- unsigned long timeout;
+ unsigned long time_left;
int ret;
unsigned int negotiated_rev;
@@ -968,10 +969,10 @@ static int tcpm_pd_transmit(struct tcpm_port *port,
return ret;
mutex_unlock(&port->lock);
- timeout = wait_for_completion_timeout(&port->tx_complete,
- msecs_to_jiffies(PD_T_TCPC_TX_TIMEOUT));
+ time_left = wait_for_completion_timeout(&port->tx_complete,
+ msecs_to_jiffies(PD_T_TCPC_TX_TIMEOUT));
mutex_lock(&port->lock);
- if (!timeout)
+ if (!time_left)
return -ETIMEDOUT;
switch (port->tx_status) {
@@ -3110,7 +3111,8 @@ static void tcpm_pd_data_request(struct tcpm_port *port,
PD_MSG_CTRL_REJECT :
PD_MSG_CTRL_NOT_SUPP,
NONE_AMS);
- } else if (port->state == SNK_WAIT_CAPABILITIES) {
+ } else if (port->state == SNK_WAIT_CAPABILITIES ||
+ port->state == SNK_WAIT_CAPABILITIES_TIMEOUT) {
/*
* This message may be received even if VBUS is not
* present. This is quite unexpected; see USB PD
@@ -5041,10 +5043,31 @@ static void run_state_machine(struct tcpm_port *port)
tcpm_set_state(port, SNK_SOFT_RESET,
PD_T_SINK_WAIT_CAP);
} else {
- tcpm_set_state(port, hard_reset_state(port),
+ tcpm_set_state(port, SNK_WAIT_CAPABILITIES_TIMEOUT,
PD_T_SINK_WAIT_CAP);
}
break;
+ case SNK_WAIT_CAPABILITIES_TIMEOUT:
+ /*
+ * There are some USB PD sources in the field, which do not
+ * properly implement the specification and fail to start
+ * sending Source Capability messages after a soft reset. The
+ * specification suggests to do a hard reset when no Source
+ * capability message is received within PD_T_SINK_WAIT_CAP,
+ * but that might effectively kil the machine's power source.
+ *
+ * This slightly diverges from the specification and tries to
+ * recover from this by explicitly asking for the capabilities
+ * using the Get_Source_Cap control message before falling back
+ * to a hard reset. The control message should also be supported
+ * and handled by all USB PD source and dual role devices
+ * according to the specification.
+ */
+ if (tcpm_pd_send_control(port, PD_CTRL_GET_SOURCE_CAP, TCPC_TX_SOP))
+ tcpm_set_state_cond(port, hard_reset_state(port), 0);
+ else
+ tcpm_set_state(port, hard_reset_state(port), PD_T_SINK_WAIT_CAP);
+ break;
case SNK_NEGOTIATE_CAPABILITIES:
port->pd_capable = true;
tcpm_set_partner_usb_comm_capable(port,
@@ -5187,6 +5210,8 @@ static void run_state_machine(struct tcpm_port *port)
case HARD_RESET_SEND:
if (port->ams != NONE_AMS)
tcpm_ams_finish(port);
+ if (!port->self_powered && port->port_type == TYPEC_PORT_SNK)
+ dev_err(port->dev, "Initiating hard-reset, which might result in machine power-loss.\n");
/*
* State machine will be directed to HARD_RESET_START,
* thus set upcoming_state to INVALID_STATE.
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
index ad76dbd20e65..ea768b19a7f1 100644
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -892,19 +892,19 @@ tps6598x_register_port(struct tps6598x *tps, struct fwnode_handle *fwnode)
return 0;
}
-static int tps_request_firmware(struct tps6598x *tps, const struct firmware **fw)
+static int tps_request_firmware(struct tps6598x *tps, const struct firmware **fw,
+ const char **firmware_name)
{
- const char *firmware_name;
int ret;
ret = device_property_read_string(tps->dev, "firmware-name",
- &firmware_name);
+ firmware_name);
if (ret)
return ret;
- ret = request_firmware(fw, firmware_name, tps->dev);
+ ret = request_firmware(fw, *firmware_name, tps->dev);
if (ret) {
- dev_err(tps->dev, "failed to retrieve \"%s\"\n", firmware_name);
+ dev_err(tps->dev, "failed to retrieve \"%s\"\n", *firmware_name);
return ret;
}
@@ -999,12 +999,7 @@ static int tps25750_start_patch_burst_mode(struct tps6598x *tps)
u32 addr;
struct device_node *np = tps->dev->of_node;
- ret = device_property_read_string(tps->dev, "firmware-name",
- &firmware_name);
- if (ret)
- return ret;
-
- ret = tps_request_firmware(tps, &fw);
+ ret = tps_request_firmware(tps, &fw, &firmware_name);
if (ret)
return ret;
@@ -1155,12 +1150,7 @@ static int tps6598x_apply_patch(struct tps6598x *tps)
const char *firmware_name;
int ret;
- ret = device_property_read_string(tps->dev, "firmware-name",
- &firmware_name);
- if (ret)
- return ret;
-
- ret = tps_request_firmware(tps, &fw);
+ ret = tps_request_firmware(tps, &fw, &firmware_name);
if (ret)
return ret;
@@ -1175,10 +1165,7 @@ static int tps6598x_apply_patch(struct tps6598x *tps)
bytes_left = fw->size;
while (bytes_left) {
- if (bytes_left < TPS_MAX_LEN)
- in_len = bytes_left;
- else
- in_len = TPS_MAX_LEN;
+ in_len = min(bytes_left, TPS_MAX_LEN);
ret = tps6598x_exec_cmd(tps, "PTCd", in_len,
fw->data + copied_bytes,
TPS_PTCD_OUT_BYTES, out);
@@ -1205,6 +1192,10 @@ static int tps6598x_apply_patch(struct tps6598x *tps)
release_fw:
release_firmware(fw);
+ if (ret) {
+ dev_err(tps->dev, "Failed to write patch %s of %zu bytes\n",
+ firmware_name, fw->size);
+ }
return ret;
};
diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig
index bdcb1764cfae..680e1b87b152 100644
--- a/drivers/usb/typec/ucsi/Kconfig
+++ b/drivers/usb/typec/ucsi/Kconfig
@@ -69,4 +69,13 @@ config UCSI_PMIC_GLINK
To compile the driver as a module, choose M here: the module will be
called ucsi_glink.
+config UCSI_LENOVO_YOGA_C630
+ tristate "UCSI Interface Driver for Lenovo Yoga C630"
+ depends on EC_LENOVO_YOGA_C630
+ help
+ This driver enables UCSI support on the Lenovo Yoga C630 laptop.
+
+ To compile the driver as a module, choose M here: the module will be
+ called ucsi_yoga_c630.
+
endif
diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile
index b4679f94696b..aed41d23887b 100644
--- a/drivers/usb/typec/ucsi/Makefile
+++ b/drivers/usb/typec/ucsi/Makefile
@@ -21,3 +21,4 @@ obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o
obj-$(CONFIG_UCSI_CCG) += ucsi_ccg.o
obj-$(CONFIG_UCSI_STM32G0) += ucsi_stm32g0.o
obj-$(CONFIG_UCSI_PMIC_GLINK) += ucsi_glink.o
+obj-$(CONFIG_UCSI_LENOVO_YOGA_C630) += ucsi_yoga_c630.o
diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c
index 8be92fc1d12c..420af5139c70 100644
--- a/drivers/usb/typec/ucsi/displayport.c
+++ b/drivers/usb/typec/ucsi/displayport.c
@@ -333,7 +333,7 @@ struct typec_altmode *ucsi_register_displayport(struct ucsi_connector *con,
dp->con = con;
dp->alt = alt;
- alt->ops = &ucsi_displayport_ops;
+ typec_altmode_set_ops(alt, &ucsi_displayport_ops);
typec_altmode_set_drvdata(alt, dp);
return alt;
diff --git a/drivers/usb/typec/ucsi/psy.c b/drivers/usb/typec/ucsi/psy.c
index b35c6e07911e..e623d80e177c 100644
--- a/drivers/usb/typec/ucsi/psy.c
+++ b/drivers/usb/typec/ucsi/psy.c
@@ -20,6 +20,7 @@ enum ucsi_psy_online_states {
};
static enum power_supply_property ucsi_psy_props[] = {
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_USB_TYPE,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_VOLTAGE_MIN,
@@ -194,6 +195,35 @@ static int ucsi_psy_get_usb_type(struct ucsi_connector *con,
return 0;
}
+static int ucsi_psy_get_charge_type(struct ucsi_connector *con, union power_supply_propval *val)
+{
+ if (!(con->status.flags & UCSI_CONSTAT_CONNECTED)) {
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ return 0;
+ }
+
+ /* The Battery Charging Cabability Status field is only valid in sink role. */
+ if ((con->status.flags & UCSI_CONSTAT_PWR_DIR) != TYPEC_SINK) {
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
+ return 0;
+ }
+
+ switch (UCSI_CONSTAT_BC_STATUS(con->status.pwr_status)) {
+ case UCSI_CONSTAT_BC_NOMINAL_CHARGING:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
+ break;
+ case UCSI_CONSTAT_BC_SLOW_CHARGING:
+ case UCSI_CONSTAT_BC_TRICKLE_CHARGING:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+ break;
+ default:
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ break;
+ }
+
+ return 0;
+}
+
static int ucsi_psy_get_prop(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
@@ -201,6 +231,8 @@ static int ucsi_psy_get_prop(struct power_supply *psy,
struct ucsi_connector *con = power_supply_get_drvdata(psy);
switch (psp) {
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ return ucsi_psy_get_charge_type(con, val);
case POWER_SUPPLY_PROP_USB_TYPE:
return ucsi_psy_get_usb_type(con, val);
case POWER_SUPPLY_PROP_ONLINE:
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 2cc7aedd490f..dcd3765cc1f5 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -36,18 +36,47 @@
*/
#define UCSI_SWAP_TIMEOUT_MS 5000
-static int ucsi_read_message_in(struct ucsi *ucsi, void *buf,
- size_t buf_size)
+void ucsi_notify_common(struct ucsi *ucsi, u32 cci)
{
- /*
- * Below UCSI 2.0, MESSAGE_IN was limited to 16 bytes. Truncate the
- * reads here.
- */
- if (ucsi->version <= UCSI_VERSION_1_2)
- buf_size = clamp(buf_size, 0, 16);
+ if (UCSI_CCI_CONNECTOR(cci))
+ ucsi_connector_change(ucsi, UCSI_CCI_CONNECTOR(cci));
+
+ if (cci & UCSI_CCI_ACK_COMPLETE &&
+ test_bit(ACK_PENDING, &ucsi->flags))
+ complete(&ucsi->complete);
- return ucsi->ops->read(ucsi, UCSI_MESSAGE_IN, buf, buf_size);
+ if (cci & UCSI_CCI_COMMAND_COMPLETE &&
+ test_bit(COMMAND_PENDING, &ucsi->flags))
+ complete(&ucsi->complete);
}
+EXPORT_SYMBOL_GPL(ucsi_notify_common);
+
+int ucsi_sync_control_common(struct ucsi *ucsi, u64 command)
+{
+ bool ack = UCSI_COMMAND(command) == UCSI_ACK_CC_CI;
+ int ret;
+
+ if (ack)
+ set_bit(ACK_PENDING, &ucsi->flags);
+ else
+ set_bit(COMMAND_PENDING, &ucsi->flags);
+
+ ret = ucsi->ops->async_control(ucsi, command);
+ if (ret)
+ goto out_clear_bit;
+
+ if (!wait_for_completion_timeout(&ucsi->complete, 5 * HZ))
+ ret = -ETIMEDOUT;
+
+out_clear_bit:
+ if (ack)
+ clear_bit(ACK_PENDING, &ucsi->flags);
+ else
+ clear_bit(COMMAND_PENDING, &ucsi->flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ucsi_sync_control_common);
static int ucsi_acknowledge(struct ucsi *ucsi, bool conn_ack)
{
@@ -60,33 +89,80 @@ static int ucsi_acknowledge(struct ucsi *ucsi, bool conn_ack)
ctrl |= UCSI_ACK_CONNECTOR_CHANGE;
}
- return ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &ctrl, sizeof(ctrl));
+ return ucsi->ops->sync_control(ucsi, ctrl);
}
-static int ucsi_exec_command(struct ucsi *ucsi, u64 command);
-
-static int ucsi_read_error(struct ucsi *ucsi)
+static int ucsi_run_command(struct ucsi *ucsi, u64 command, u32 *cci,
+ void *data, size_t size, bool conn_ack)
{
- u16 error;
- int ret;
+ int ret, err;
+
+ *cci = 0;
+
+ /*
+ * Below UCSI 2.0, MESSAGE_IN was limited to 16 bytes. Truncate the
+ * reads here.
+ */
+ if (ucsi->version <= UCSI_VERSION_1_2)
+ size = clamp(size, 0, 16);
- /* Acknowledge the command that failed */
- ret = ucsi_acknowledge(ucsi, false);
+ ret = ucsi->ops->sync_control(ucsi, command);
if (ret)
return ret;
- ret = ucsi_exec_command(ucsi, UCSI_GET_ERROR_STATUS);
- if (ret < 0)
+ ret = ucsi->ops->read_cci(ucsi, cci);
+ if (ret)
return ret;
- ret = ucsi_read_message_in(ucsi, &error, sizeof(error));
+ if (*cci & UCSI_CCI_BUSY)
+ return -EBUSY;
+
+ if (!(*cci & UCSI_CCI_COMMAND_COMPLETE))
+ return -EIO;
+
+ if (*cci & UCSI_CCI_NOT_SUPPORTED)
+ err = -EOPNOTSUPP;
+ else if (*cci & UCSI_CCI_ERROR)
+ err = -EIO;
+ else
+ err = 0;
+
+ if (!err && data && UCSI_CCI_LENGTH(*cci))
+ err = ucsi->ops->read_message_in(ucsi, data, size);
+
+ /*
+ * Don't ACK connection change if there was an error.
+ */
+ ret = ucsi_acknowledge(ucsi, err ? false : conn_ack);
if (ret)
return ret;
- ret = ucsi_acknowledge(ucsi, false);
- if (ret)
+ return err;
+}
+
+static int ucsi_read_error(struct ucsi *ucsi, u8 connector_num)
+{
+ u64 command;
+ u16 error;
+ u32 cci;
+ int ret;
+
+ command = UCSI_GET_ERROR_STATUS | UCSI_CONNECTOR_NUMBER(connector_num);
+ ret = ucsi_run_command(ucsi, command, &cci,
+ &error, sizeof(error), false);
+
+ if (cci & UCSI_CCI_BUSY) {
+ ret = ucsi_run_command(ucsi, UCSI_CANCEL, &cci, NULL, 0, false);
+
+ return ret ? ret : -EBUSY;
+ }
+
+ if (ret < 0)
return ret;
+ if (cci & UCSI_CCI_ERROR)
+ return -EIO;
+
switch (error) {
case UCSI_ERROR_INCOMPATIBLE_PARTNER:
return -EOPNOTSUPP;
@@ -117,6 +193,12 @@ static int ucsi_read_error(struct ucsi *ucsi)
case UCSI_ERROR_SWAP_REJECTED:
dev_warn(ucsi->dev, "Swap rejected\n");
break;
+ case UCSI_ERROR_REVERSE_CURRENT_PROTECTION:
+ dev_warn(ucsi->dev, "Reverse Current Protection detected\n");
+ break;
+ case UCSI_ERROR_SET_SINK_PATH_REJECTED:
+ dev_warn(ucsi->dev, "Set Sink Path rejected\n");
+ break;
case UCSI_ERROR_UNDEFINED:
default:
dev_err(ucsi->dev, "unknown error %u\n", error);
@@ -126,77 +208,44 @@ static int ucsi_read_error(struct ucsi *ucsi)
return -EIO;
}
-static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd)
+static int ucsi_send_command_common(struct ucsi *ucsi, u64 cmd,
+ void *data, size_t size, bool conn_ack)
{
+ u8 connector_num;
u32 cci;
int ret;
- ret = ucsi->ops->sync_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd));
- if (ret)
- return ret;
-
- ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci));
- if (ret)
- return ret;
-
- if (cmd != UCSI_CANCEL && cci & UCSI_CCI_BUSY)
- return ucsi_exec_command(ucsi, UCSI_CANCEL);
-
- if (!(cci & UCSI_CCI_COMMAND_COMPLETE))
- return -EIO;
-
- if (cci & UCSI_CCI_NOT_SUPPORTED) {
- if (ucsi_acknowledge(ucsi, false) < 0)
- dev_err(ucsi->dev,
- "ACK of unsupported command failed\n");
- return -EOPNOTSUPP;
- }
-
- if (cci & UCSI_CCI_ERROR) {
- if (cmd == UCSI_GET_ERROR_STATUS) {
- ret = ucsi_acknowledge(ucsi, false);
- if (ret)
- return ret;
-
- return -EIO;
+ if (ucsi->version > UCSI_VERSION_1_2) {
+ switch (UCSI_COMMAND(cmd)) {
+ case UCSI_GET_ALTERNATE_MODES:
+ connector_num = UCSI_GET_ALTMODE_GET_CONNECTOR_NUMBER(cmd);
+ break;
+ case UCSI_PPM_RESET:
+ case UCSI_CANCEL:
+ case UCSI_ACK_CC_CI:
+ case UCSI_SET_NOTIFICATION_ENABLE:
+ case UCSI_GET_CAPABILITY:
+ connector_num = 0;
+ break;
+ default:
+ connector_num = UCSI_DEFAULT_GET_CONNECTOR_NUMBER(cmd);
+ break;
}
- return ucsi_read_error(ucsi);
- }
-
- if (cmd == UCSI_CANCEL && cci & UCSI_CCI_CANCEL_COMPLETE) {
- ret = ucsi_acknowledge(ucsi, false);
- return ret ? ret : -EBUSY;
+ } else {
+ connector_num = 0;
}
- return UCSI_CCI_LENGTH(cci);
-}
-
-static int ucsi_send_command_common(struct ucsi *ucsi, u64 command,
- void *data, size_t size, bool conn_ack)
-{
- u8 length;
- int ret;
-
mutex_lock(&ucsi->ppm_lock);
- ret = ucsi_exec_command(ucsi, command);
- if (ret < 0)
- goto out;
-
- length = ret;
-
- if (data) {
- ret = ucsi_read_message_in(ucsi, data, size);
- if (ret)
- goto out;
+ ret = ucsi_run_command(ucsi, cmd, &cci, data, size, conn_ack);
+ if (cci & UCSI_CCI_BUSY) {
+ ret = ucsi_run_command(ucsi, UCSI_CANCEL, &cci, NULL, 0, false);
+ return ret ? ret : -EBUSY;
}
- ret = ucsi_acknowledge(ucsi, conn_ack);
- if (ret)
- goto out;
+ if (cci & UCSI_CCI_ERROR)
+ return ucsi_read_error(ucsi, connector_num);
- ret = length;
-out:
mutex_unlock(&ucsi->ppm_lock);
return ret;
}
@@ -646,9 +695,13 @@ static int ucsi_read_pdos(struct ucsi_connector *con,
static int ucsi_get_pdos(struct ucsi_connector *con, enum typec_role role,
int is_partner, u32 *pdos)
{
+ struct ucsi *ucsi = con->ucsi;
u8 num_pdos;
int ret;
+ if (!(ucsi->cap.features & UCSI_CAP_PDO_DETAILS))
+ return 0;
+
/* UCSI max payload means only getting at most 4 PDOs at a time */
ret = ucsi_read_pdos(con, role, is_partner, pdos, 0, UCSI_MAX_PDOS);
if (ret < 0)
@@ -817,10 +870,11 @@ static int ucsi_check_altmodes(struct ucsi_connector *con)
/* Ignoring the errors in this case. */
if (con->partner_altmode[0]) {
num_partner_am = ucsi_get_num_altmode(con->partner_altmode);
- if (num_partner_am > 0)
- typec_partner_set_num_altmodes(con->partner, num_partner_am);
+ typec_partner_set_num_altmodes(con->partner, num_partner_am);
ucsi_altmode_update_active(con);
return 0;
+ } else {
+ typec_partner_set_num_altmodes(con->partner, 0);
}
return ret;
@@ -968,7 +1022,7 @@ static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
con->rdo = con->status.request_data_obj;
typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_PD);
ucsi_partner_task(con, ucsi_get_src_pdos, 30, 0);
- ucsi_partner_task(con, ucsi_check_altmodes, 30, 0);
+ ucsi_partner_task(con, ucsi_check_altmodes, 30, HZ);
ucsi_partner_task(con, ucsi_register_partner_pdos, 1, HZ);
break;
case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
@@ -1143,7 +1197,7 @@ static int ucsi_check_connection(struct ucsi_connector *con)
static int ucsi_check_cable(struct ucsi_connector *con)
{
u64 command;
- int ret;
+ int ret, num_plug_am;
if (con->cable)
return 0;
@@ -1175,6 +1229,13 @@ static int ucsi_check_cable(struct ucsi_connector *con)
ret = ucsi_register_altmodes(con, UCSI_RECIPIENT_SOP_P);
if (ret < 0)
return ret;
+
+ if (con->plug_altmode[0]) {
+ num_plug_am = ucsi_get_num_altmode(con->plug_altmode);
+ typec_plug_set_num_altmodes(con->plug, num_plug_am);
+ } else {
+ typec_plug_set_num_altmodes(con->plug, 0);
+ }
}
return 0;
@@ -1252,7 +1313,10 @@ static void ucsi_handle_connector_change(struct work_struct *work)
}
if (con->status.change & UCSI_CONSTAT_CAM_CHANGE)
- ucsi_partner_task(con, ucsi_check_altmodes, 1, 0);
+ ucsi_partner_task(con, ucsi_check_altmodes, 1, HZ);
+
+ if (con->status.change & UCSI_CONSTAT_BC_CHANGE)
+ ucsi_port_psy_changed(con);
out_unlock:
mutex_unlock(&con->lock);
@@ -1298,7 +1362,7 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
mutex_lock(&ucsi->ppm_lock);
- ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci));
+ ret = ucsi->ops->read_cci(ucsi, &cci);
if (ret < 0)
goto out;
@@ -1310,15 +1374,13 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
*/
if (cci & UCSI_CCI_RESET_COMPLETE) {
command = UCSI_SET_NOTIFICATION_ENABLE;
- ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, &command,
- sizeof(command));
+ ret = ucsi->ops->async_control(ucsi, command);
if (ret < 0)
goto out;
tmo = jiffies + msecs_to_jiffies(UCSI_TIMEOUT_MS);
do {
- ret = ucsi->ops->read(ucsi, UCSI_CCI,
- &cci, sizeof(cci));
+ ret = ucsi->ops->read_cci(ucsi, &cci);
if (ret < 0)
goto out;
if (cci & UCSI_CCI_COMMAND_COMPLETE)
@@ -1332,8 +1394,7 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
}
command = UCSI_PPM_RESET;
- ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL, &command,
- sizeof(command));
+ ret = ucsi->ops->async_control(ucsi, command);
if (ret < 0)
goto out;
@@ -1348,15 +1409,13 @@ static int ucsi_reset_ppm(struct ucsi *ucsi)
/* Give the PPM time to process a reset before reading CCI */
msleep(20);
- ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci));
+ ret = ucsi->ops->read_cci(ucsi, &cci);
if (ret)
goto out;
/* If the PPM is still doing something else, reset it again. */
if (cci & ~UCSI_CCI_RESET_COMPLETE) {
- ret = ucsi->ops->async_write(ucsi, UCSI_CONTROL,
- &command,
- sizeof(command));
+ ret = ucsi->ops->async_control(ucsi, command);
if (ret < 0)
goto out;
}
@@ -1669,7 +1728,7 @@ out_unlock:
static u64 ucsi_get_supported_notifications(struct ucsi *ucsi)
{
- u8 features = ucsi->cap.features;
+ u16 features = ucsi->cap.features;
u64 ntfy = UCSI_ENABLE_NTFY_ALL;
if (!(features & UCSI_CAP_ALT_MODE_DETAILS))
@@ -1685,6 +1744,23 @@ static u64 ucsi_get_supported_notifications(struct ucsi *ucsi)
if (!(features & UCSI_CAP_PD_RESET))
ntfy &= ~UCSI_ENABLE_NTFY_PD_RESET_COMPLETE;
+ if (ucsi->version <= UCSI_VERSION_1_2)
+ return ntfy;
+
+ ntfy |= UCSI_ENABLE_NTFY_SINK_PATH_STS_CHANGE;
+
+ if (features & UCSI_CAP_GET_ATTENTION_VDO)
+ ntfy |= UCSI_ENABLE_NTFY_ATTENTION;
+
+ if (features & UCSI_CAP_FW_UPDATE_REQUEST)
+ ntfy |= UCSI_ENABLE_NTFY_LPM_FW_UPDATE_REQ;
+
+ if (features & UCSI_CAP_SECURITY_REQUEST)
+ ntfy |= UCSI_ENABLE_NTFY_SECURITY_REQ_PARTNER;
+
+ if (features & UCSI_CAP_SET_RETIMER_MODE)
+ ntfy |= UCSI_ENABLE_NTFY_SET_RETIMER_MODE;
+
return ntfy;
}
@@ -1753,7 +1829,7 @@ static int ucsi_init(struct ucsi *ucsi)
ucsi->ntfy = ntfy;
mutex_lock(&ucsi->ppm_lock);
- ret = ucsi->ops->read(ucsi, UCSI_CCI, &cci, sizeof(cci));
+ ret = ucsi->ops->read_cci(ucsi, &cci);
mutex_unlock(&ucsi->ppm_lock);
if (ret)
return ret;
@@ -1867,7 +1943,9 @@ struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops)
{
struct ucsi *ucsi;
- if (!ops || !ops->read || !ops->sync_write || !ops->async_write)
+ if (!ops ||
+ !ops->read_version || !ops->read_cci || !ops->read_message_in ||
+ !ops->sync_control || !ops->async_control)
return ERR_PTR(-EINVAL);
ucsi = kzalloc(sizeof(*ucsi), GFP_KERNEL);
@@ -1877,6 +1955,7 @@ struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops)
INIT_WORK(&ucsi->resume_work, ucsi_resume_work);
INIT_DELAYED_WORK(&ucsi->work, ucsi_init_work);
mutex_init(&ucsi->ppm_lock);
+ init_completion(&ucsi->complete);
ucsi->dev = dev;
ucsi->ops = ops;
@@ -1903,8 +1982,7 @@ int ucsi_register(struct ucsi *ucsi)
{
int ret;
- ret = ucsi->ops->read(ucsi, UCSI_VERSION, &ucsi->version,
- sizeof(ucsi->version));
+ ret = ucsi->ops->read_version(ucsi, &ucsi->version);
if (ret)
return ret;
@@ -1943,7 +2021,7 @@ void ucsi_unregister(struct ucsi *ucsi)
cancel_work_sync(&ucsi->resume_work);
/* Disable notifications */
- ucsi->ops->async_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd));
+ ucsi->ops->async_control(ucsi, cmd);
if (!ucsi->connector)
return;
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index c4d103db9d0f..57129f3c0814 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -4,6 +4,7 @@
#define __DRIVER_USB_TYPEC_UCSI_H
#include <linux/bitops.h>
+#include <linux/completion.h>
#include <linux/device.h>
#include <linux/power_supply.h>
#include <linux/types.h>
@@ -56,9 +57,11 @@ struct dentry;
/**
* struct ucsi_operations - UCSI I/O operations
- * @read: Read operation
- * @sync_write: Blocking write operation
- * @async_write: Non-blocking write operation
+ * @read_version: Read implemented UCSI version
+ * @read_cci: Read CCI register
+ * @read_message_in: Read message data from UCSI
+ * @sync_control: Blocking control operation
+ * @async_control: Non-blocking control operation
* @update_altmodes: Squashes duplicate DP altmodes
* @update_connector: Update connector capabilities before registering
* @connector_status: Updates connector status, called holding connector lock
@@ -68,12 +71,11 @@ struct dentry;
* return immediately after sending the data to the PPM.
*/
struct ucsi_operations {
- int (*read)(struct ucsi *ucsi, unsigned int offset,
- void *val, size_t val_len);
- int (*sync_write)(struct ucsi *ucsi, unsigned int offset,
- const void *val, size_t val_len);
- int (*async_write)(struct ucsi *ucsi, unsigned int offset,
- const void *val, size_t val_len);
+ int (*read_version)(struct ucsi *ucsi, u16 *version);
+ int (*read_cci)(struct ucsi *ucsi, u32 *cci);
+ int (*read_message_in)(struct ucsi *ucsi, void *val, size_t val_len);
+ int (*sync_control)(struct ucsi *ucsi, u64 command);
+ int (*async_control)(struct ucsi *ucsi, u64 command);
bool (*update_altmodes)(struct ucsi *ucsi, struct ucsi_altmode *orig,
struct ucsi_altmode *updated);
void (*update_connector)(struct ucsi_connector *con);
@@ -116,6 +118,9 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
#define UCSI_CONNECTOR_NUMBER(_num_) ((u64)(_num_) << 16)
#define UCSI_COMMAND(_cmd_) ((_cmd_) & 0xff)
+#define UCSI_GET_ALTMODE_GET_CONNECTOR_NUMBER(_cmd_) (((_cmd_) >> 24) & GENMASK(6, 0))
+#define UCSI_DEFAULT_GET_CONNECTOR_NUMBER(_cmd_) (((_cmd_) >> 16) & GENMASK(6, 0))
+
/* CONNECTOR_RESET command bits */
#define UCSI_CONNECTOR_RESET_HARD BIT(23) /* Deprecated in v1.1 */
@@ -124,18 +129,23 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
#define UCSI_ACK_COMMAND_COMPLETE BIT(17)
/* SET_NOTIFICATION_ENABLE command bits */
-#define UCSI_ENABLE_NTFY_CMD_COMPLETE BIT(16)
-#define UCSI_ENABLE_NTFY_EXT_PWR_SRC_CHANGE BIT(17)
-#define UCSI_ENABLE_NTFY_PWR_OPMODE_CHANGE BIT(18)
-#define UCSI_ENABLE_NTFY_CAP_CHANGE BIT(21)
-#define UCSI_ENABLE_NTFY_PWR_LEVEL_CHANGE BIT(22)
-#define UCSI_ENABLE_NTFY_PD_RESET_COMPLETE BIT(23)
-#define UCSI_ENABLE_NTFY_CAM_CHANGE BIT(24)
-#define UCSI_ENABLE_NTFY_BAT_STATUS_CHANGE BIT(25)
-#define UCSI_ENABLE_NTFY_PARTNER_CHANGE BIT(27)
-#define UCSI_ENABLE_NTFY_PWR_DIR_CHANGE BIT(28)
-#define UCSI_ENABLE_NTFY_CONNECTOR_CHANGE BIT(30)
-#define UCSI_ENABLE_NTFY_ERROR BIT(31)
+#define UCSI_ENABLE_NTFY_CMD_COMPLETE BIT_ULL(16)
+#define UCSI_ENABLE_NTFY_EXT_PWR_SRC_CHANGE BIT_ULL(17)
+#define UCSI_ENABLE_NTFY_PWR_OPMODE_CHANGE BIT_ULL(18)
+#define UCSI_ENABLE_NTFY_ATTENTION BIT_ULL(19)
+#define UCSI_ENABLE_NTFY_LPM_FW_UPDATE_REQ BIT_ULL(20)
+#define UCSI_ENABLE_NTFY_CAP_CHANGE BIT_ULL(21)
+#define UCSI_ENABLE_NTFY_PWR_LEVEL_CHANGE BIT_ULL(22)
+#define UCSI_ENABLE_NTFY_PD_RESET_COMPLETE BIT_ULL(23)
+#define UCSI_ENABLE_NTFY_CAM_CHANGE BIT_ULL(24)
+#define UCSI_ENABLE_NTFY_BAT_STATUS_CHANGE BIT_ULL(25)
+#define UCSI_ENABLE_NTFY_SECURITY_REQ_PARTNER BIT_ULL(26)
+#define UCSI_ENABLE_NTFY_PARTNER_CHANGE BIT_ULL(27)
+#define UCSI_ENABLE_NTFY_PWR_DIR_CHANGE BIT_ULL(28)
+#define UCSI_ENABLE_NTFY_SET_RETIMER_MODE BIT_ULL(29)
+#define UCSI_ENABLE_NTFY_CONNECTOR_CHANGE BIT_ULL(30)
+#define UCSI_ENABLE_NTFY_ERROR BIT_ULL(31)
+#define UCSI_ENABLE_NTFY_SINK_PATH_STS_CHANGE BIT_ULL(32)
#define UCSI_ENABLE_NTFY_ALL 0xdbe70000
/* SET_UOR command bits */
@@ -193,6 +203,8 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
#define UCSI_ERROR_HARD_RESET BIT(10)
#define UCSI_ERROR_PPM_POLICY_CONFLICT BIT(11)
#define UCSI_ERROR_SWAP_REJECTED BIT(12)
+#define UCSI_ERROR_REVERSE_CURRENT_PROTECTION BIT(13)
+#define UCSI_ERROR_SET_SINK_PATH_REJECTED BIT(14)
#define UCSI_SET_NEW_CAM_ENTER(x) (((x) >> 23) & 0x1)
#define UCSI_SET_NEW_CAM_GET_AM(x) (((x) >> 24) & 0xff)
@@ -220,7 +232,13 @@ struct ucsi_capability {
#define UCSI_CAP_CABLE_DETAILS BIT(5)
#define UCSI_CAP_EXT_SUPPLY_NOTIFICATIONS BIT(6)
#define UCSI_CAP_PD_RESET BIT(7)
-#define UCSI_CAP_GET_PD_MESSAGE BIT(8)
+#define UCSI_CAP_GET_PD_MESSAGE BIT(8)
+#define UCSI_CAP_GET_ATTENTION_VDO BIT(9)
+#define UCSI_CAP_FW_UPDATE_REQUEST BIT(10)
+#define UCSI_CAP_NEGOTIATED_PWR_LEVEL_CHANGE BIT(11)
+#define UCSI_CAP_SECURITY_REQUEST BIT(12)
+#define UCSI_CAP_SET_RETIMER_MODE BIT(13)
+#define UCSI_CAP_CHUNKING_SUPPORT BIT(14)
u8 reserved_1;
u8 num_alt_modes;
u8 reserved_2;
@@ -384,7 +402,7 @@ struct ucsi_debugfs_entry {
struct ucsi {
u16 version;
struct device *dev;
- struct driver_data *driver_data;
+ void *driver_data;
const struct ucsi_operations *ops;
@@ -408,6 +426,9 @@ struct ucsi {
/* PPM communication flags */
unsigned long flags;
#define EVENT_PENDING 0
+#define COMMAND_PENDING 1
+#define ACK_PENDING 2
+ struct completion complete;
unsigned long quirks;
#define UCSI_NO_PARTNER_PDOS BIT(0) /* Don't read partner's PDOs */
@@ -472,6 +493,9 @@ int ucsi_send_command(struct ucsi *ucsi, u64 command,
void ucsi_altmode_update_active(struct ucsi_connector *con);
int ucsi_resume(struct ucsi *ucsi);
+void ucsi_notify_common(struct ucsi *ucsi, u32 cci);
+int ucsi_sync_control_common(struct ucsi *ucsi, u64 command);
+
#if IS_ENABLED(CONFIG_POWER_SUPPLY)
int ucsi_register_port_psy(struct ucsi_connector *con);
void ucsi_unregister_port_psy(struct ucsi_connector *con);
@@ -496,7 +520,7 @@ ucsi_register_displayport(struct ucsi_connector *con,
bool override, int offset,
struct typec_altmode_desc *desc)
{
- return NULL;
+ return typec_port_register_altmode(con->port, desc);
}
static inline void
diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c
index adf32ca0f761..7a5dff8d9cc6 100644
--- a/drivers/usb/typec/ucsi/ucsi_acpi.c
+++ b/drivers/usb/typec/ucsi/ucsi_acpi.c
@@ -21,11 +21,7 @@ struct ucsi_acpi {
struct device *dev;
struct ucsi *ucsi;
void *base;
- struct completion complete;
- unsigned long flags;
-#define UCSI_ACPI_COMMAND_PENDING 1
-#define UCSI_ACPI_ACK_PENDING 2
-#define UCSI_ACPI_CHECK_BOGUS_EVENT 3
+ bool check_bogus_event;
guid_t guid;
u64 cmd;
};
@@ -46,8 +42,7 @@ static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func)
return 0;
}
-static int ucsi_acpi_read(struct ucsi *ucsi, unsigned int offset,
- void *val, size_t val_len)
+static int ucsi_acpi_read_version(struct ucsi *ucsi, u16 *version)
{
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
int ret;
@@ -56,81 +51,94 @@ static int ucsi_acpi_read(struct ucsi *ucsi, unsigned int offset,
if (ret)
return ret;
- memcpy(val, ua->base + offset, val_len);
+ memcpy(version, ua->base + UCSI_VERSION, sizeof(*version));
return 0;
}
-static int ucsi_acpi_async_write(struct ucsi *ucsi, unsigned int offset,
- const void *val, size_t val_len)
+static int ucsi_acpi_read_cci(struct ucsi *ucsi, u32 *cci)
{
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
+ int ret;
- memcpy(ua->base + offset, val, val_len);
- ua->cmd = *(u64 *)val;
+ ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
+ if (ret)
+ return ret;
- return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_WRITE);
+ memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci));
+
+ return 0;
}
-static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
- const void *val, size_t val_len)
+static int ucsi_acpi_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
{
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
- bool ack = UCSI_COMMAND(*(u64 *)val) == UCSI_ACK_CC_CI;
int ret;
- if (ack)
- set_bit(UCSI_ACPI_ACK_PENDING, &ua->flags);
- else
- set_bit(UCSI_ACPI_COMMAND_PENDING, &ua->flags);
-
- ret = ucsi_acpi_async_write(ucsi, offset, val, val_len);
+ ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
if (ret)
- goto out_clear_bit;
+ return ret;
- if (!wait_for_completion_timeout(&ua->complete, 5 * HZ))
- ret = -ETIMEDOUT;
+ memcpy(val, ua->base + UCSI_MESSAGE_IN, val_len);
-out_clear_bit:
- if (ack)
- clear_bit(UCSI_ACPI_ACK_PENDING, &ua->flags);
- else
- clear_bit(UCSI_ACPI_COMMAND_PENDING, &ua->flags);
+ return 0;
+}
- return ret;
+static int ucsi_acpi_async_control(struct ucsi *ucsi, u64 command)
+{
+ struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
+
+ memcpy(ua->base + UCSI_CONTROL, &command, sizeof(command));
+ ua->cmd = command;
+
+ return ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_WRITE);
}
static const struct ucsi_operations ucsi_acpi_ops = {
- .read = ucsi_acpi_read,
- .sync_write = ucsi_acpi_sync_write,
- .async_write = ucsi_acpi_async_write
+ .read_version = ucsi_acpi_read_version,
+ .read_cci = ucsi_acpi_read_cci,
+ .read_message_in = ucsi_acpi_read_message_in,
+ .sync_control = ucsi_sync_control_common,
+ .async_control = ucsi_acpi_async_control
};
static int
-ucsi_zenbook_read(struct ucsi *ucsi, unsigned int offset, void *val, size_t val_len)
+ucsi_zenbook_read_cci(struct ucsi *ucsi, u32 *cci)
{
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
int ret;
- if (offset == UCSI_VERSION || UCSI_COMMAND(ua->cmd) == UCSI_PPM_RESET) {
+ if (UCSI_COMMAND(ua->cmd) == UCSI_PPM_RESET) {
ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
if (ret)
return ret;
}
- memcpy(val, ua->base + offset, val_len);
+ memcpy(cci, ua->base + UCSI_CCI, sizeof(*cci));
+
+ return 0;
+}
+
+static int
+ucsi_zenbook_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
+{
+ struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
+
+ /* UCSI_MESSAGE_IN is never read for PPM_RESET, return stored data */
+ memcpy(val, ua->base + UCSI_MESSAGE_IN, val_len);
return 0;
}
static const struct ucsi_operations ucsi_zenbook_ops = {
- .read = ucsi_zenbook_read,
- .sync_write = ucsi_acpi_sync_write,
- .async_write = ucsi_acpi_async_write
+ .read_version = ucsi_acpi_read_version,
+ .read_cci = ucsi_zenbook_read_cci,
+ .read_message_in = ucsi_zenbook_read_message_in,
+ .sync_control = ucsi_sync_control_common,
+ .async_control = ucsi_acpi_async_control
};
-static int ucsi_gram_read(struct ucsi *ucsi, unsigned int offset,
- void *val, size_t val_len)
+static int ucsi_gram_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
{
u16 bogus_change = UCSI_CONSTAT_POWER_LEVEL_CHANGE |
UCSI_CONSTAT_PDOS_CHANGE;
@@ -138,47 +146,47 @@ static int ucsi_gram_read(struct ucsi *ucsi, unsigned int offset,
struct ucsi_connector_status *status;
int ret;
- ret = ucsi_acpi_read(ucsi, offset, val, val_len);
+ ret = ucsi_acpi_read_message_in(ucsi, val, val_len);
if (ret < 0)
return ret;
if (UCSI_COMMAND(ua->cmd) == UCSI_GET_CONNECTOR_STATUS &&
- test_bit(UCSI_ACPI_CHECK_BOGUS_EVENT, &ua->flags) &&
- offset == UCSI_MESSAGE_IN) {
+ ua->check_bogus_event) {
status = (struct ucsi_connector_status *)val;
/* Clear the bogus change */
if (status->change == bogus_change)
status->change = 0;
- clear_bit(UCSI_ACPI_CHECK_BOGUS_EVENT, &ua->flags);
+ ua->check_bogus_event = false;
}
return ret;
}
-static int ucsi_gram_sync_write(struct ucsi *ucsi, unsigned int offset,
- const void *val, size_t val_len)
+static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command)
{
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
int ret;
- ret = ucsi_acpi_sync_write(ucsi, offset, val, val_len);
+ ret = ucsi_sync_control_common(ucsi, command);
if (ret < 0)
return ret;
if (UCSI_COMMAND(ua->cmd) == UCSI_GET_PDOS &&
ua->cmd & UCSI_GET_PDOS_PARTNER_PDO(1) &&
ua->cmd & UCSI_GET_PDOS_SRC_PDOS)
- set_bit(UCSI_ACPI_CHECK_BOGUS_EVENT, &ua->flags);
+ ua->check_bogus_event = true;
return ret;
}
static const struct ucsi_operations ucsi_gram_ops = {
- .read = ucsi_gram_read,
- .sync_write = ucsi_gram_sync_write,
- .async_write = ucsi_acpi_async_write
+ .read_version = ucsi_acpi_read_version,
+ .read_cci = ucsi_acpi_read_cci,
+ .read_message_in = ucsi_gram_read_message_in,
+ .sync_control = ucsi_gram_sync_control,
+ .async_control = ucsi_acpi_async_control
};
static const struct dmi_system_id ucsi_acpi_quirks[] = {
@@ -206,19 +214,11 @@ static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data)
u32 cci;
int ret;
- ret = ua->ucsi->ops->read(ua->ucsi, UCSI_CCI, &cci, sizeof(cci));
+ ret = ua->ucsi->ops->read_cci(ua->ucsi, &cci);
if (ret)
return;
- if (UCSI_CCI_CONNECTOR(cci))
- ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci));
-
- if (cci & UCSI_CCI_ACK_COMPLETE &&
- test_bit(UCSI_ACPI_ACK_PENDING, &ua->flags))
- complete(&ua->complete);
- if (cci & UCSI_CCI_COMMAND_COMPLETE &&
- test_bit(UCSI_ACPI_COMMAND_PENDING, &ua->flags))
- complete(&ua->complete);
+ ucsi_notify_common(ua->ucsi, cci);
}
static int ucsi_acpi_probe(struct platform_device *pdev)
@@ -252,7 +252,6 @@ static int ucsi_acpi_probe(struct platform_device *pdev)
if (ret)
return ret;
- init_completion(&ua->complete);
ua->dev = &pdev->dev;
id = dmi_first_match(ucsi_acpi_quirks);
diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
index dda7c7c94e08..b3ec799fc873 100644
--- a/drivers/usb/typec/ucsi/ucsi_ccg.c
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -222,8 +222,6 @@ struct ucsi_ccg {
u16 fw_build;
struct work_struct pm_work;
- struct completion complete;
-
u64 last_cmd_sent;
bool has_multiple_dp;
struct ucsi_ccg_altmode orig[UCSI_MAX_ALTMODES];
@@ -556,32 +554,34 @@ static void ucsi_ccg_nvidia_altmode(struct ucsi_ccg *uc,
}
}
-static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
- void *val, size_t val_len)
+static int ucsi_ccg_read_version(struct ucsi *ucsi, u16 *version)
{
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
- u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
- struct ucsi_capability *cap;
- struct ucsi_altmode *alt;
- int ret = 0;
+ u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_VERSION);
- if (offset == UCSI_CCI) {
- spin_lock(&uc->op_lock);
- memcpy(val, &(uc->op_data).cci, val_len);
- spin_unlock(&uc->op_lock);
- } else if (offset == UCSI_MESSAGE_IN) {
- spin_lock(&uc->op_lock);
- memcpy(val, &(uc->op_data).message_in, val_len);
- spin_unlock(&uc->op_lock);
- } else {
- ret = ccg_read(uc, reg, val, val_len);
- }
+ return ccg_read(uc, reg, (u8 *)version, sizeof(*version));
+}
- if (ret)
- return ret;
+static int ucsi_ccg_read_cci(struct ucsi *ucsi, u32 *cci)
+{
+ struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
- if (offset != UCSI_MESSAGE_IN)
- return ret;
+ spin_lock(&uc->op_lock);
+ *cci = uc->op_data.cci;
+ spin_unlock(&uc->op_lock);
+
+ return 0;
+}
+
+static int ucsi_ccg_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
+{
+ struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
+ struct ucsi_capability *cap;
+ struct ucsi_altmode *alt;
+
+ spin_lock(&uc->op_lock);
+ memcpy(val, uc->op_data.message_in, val_len);
+ spin_unlock(&uc->op_lock);
switch (UCSI_COMMAND(uc->last_cmd_sent)) {
case UCSI_GET_CURRENT_CAM:
@@ -607,28 +607,26 @@ static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
}
uc->last_cmd_sent = 0;
- return ret;
+ return 0;
}
-static int ucsi_ccg_async_write(struct ucsi *ucsi, unsigned int offset,
- const void *val, size_t val_len)
+static int ucsi_ccg_async_control(struct ucsi *ucsi, u64 command)
{
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
- u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
+ u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_CONTROL);
/*
- * UCSI may read CCI instantly after async_write,
+ * UCSI may read CCI instantly after async_control,
* clear CCI to avoid caller getting wrong data before we get CCI from ISR
*/
spin_lock(&uc->op_lock);
uc->op_data.cci = 0;
spin_unlock(&uc->op_lock);
- return ccg_write(uc, reg, val, val_len);
+ return ccg_write(uc, reg, (u8 *)&command, sizeof(command));
}
-static int ucsi_ccg_sync_write(struct ucsi *ucsi, unsigned int offset,
- const void *val, size_t val_len)
+static int ucsi_ccg_sync_control(struct ucsi *ucsi, u64 command)
{
struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
struct ucsi_connector *con;
@@ -637,29 +635,19 @@ static int ucsi_ccg_sync_write(struct ucsi *ucsi, unsigned int offset,
mutex_lock(&uc->lock);
pm_runtime_get_sync(uc->dev);
- set_bit(DEV_CMD_PENDING, &uc->flags);
- if (offset == UCSI_CONTROL && val_len == sizeof(uc->last_cmd_sent)) {
- uc->last_cmd_sent = *(u64 *)val;
+ uc->last_cmd_sent = command;
- if (UCSI_COMMAND(uc->last_cmd_sent) == UCSI_SET_NEW_CAM &&
- uc->has_multiple_dp) {
- con_index = (uc->last_cmd_sent >> 16) &
- UCSI_CMD_CONNECTOR_MASK;
- con = &uc->ucsi->connector[con_index - 1];
- ucsi_ccg_update_set_new_cam_cmd(uc, con, (u64 *)val);
- }
+ if (UCSI_COMMAND(uc->last_cmd_sent) == UCSI_SET_NEW_CAM &&
+ uc->has_multiple_dp) {
+ con_index = (uc->last_cmd_sent >> 16) &
+ UCSI_CMD_CONNECTOR_MASK;
+ con = &uc->ucsi->connector[con_index - 1];
+ ucsi_ccg_update_set_new_cam_cmd(uc, con, &command);
}
- ret = ucsi_ccg_async_write(ucsi, offset, val, val_len);
- if (ret)
- goto err_clear_bit;
-
- if (!wait_for_completion_timeout(&uc->complete, msecs_to_jiffies(5000)))
- ret = -ETIMEDOUT;
+ ret = ucsi_sync_control_common(ucsi, command);
-err_clear_bit:
- clear_bit(DEV_CMD_PENDING, &uc->flags);
pm_runtime_put_sync(uc->dev);
mutex_unlock(&uc->lock);
@@ -667,9 +655,11 @@ err_clear_bit:
}
static const struct ucsi_operations ucsi_ccg_ops = {
- .read = ucsi_ccg_read,
- .sync_write = ucsi_ccg_sync_write,
- .async_write = ucsi_ccg_async_write,
+ .read_version = ucsi_ccg_read_version,
+ .read_cci = ucsi_ccg_read_cci,
+ .read_message_in = ucsi_ccg_read_message_in,
+ .sync_control = ucsi_ccg_sync_control,
+ .async_control = ucsi_ccg_async_control,
.update_altmodes = ucsi_ccg_update_altmodes
};
@@ -694,9 +684,6 @@ static irqreturn_t ccg_irq_handler(int irq, void *data)
if (ret)
goto err_clear_irq;
- if (UCSI_CCI_CONNECTOR(cci))
- ucsi_connector_change(uc->ucsi, UCSI_CCI_CONNECTOR(cci));
-
/*
* As per CCGx UCSI interface guide, copy CCI and MESSAGE_IN
* to the OpRegion before clear the UCSI interrupt
@@ -708,9 +695,8 @@ static irqreturn_t ccg_irq_handler(int irq, void *data)
err_clear_irq:
ccg_write(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg));
- if (!ret && test_bit(DEV_CMD_PENDING, &uc->flags) &&
- cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE))
- complete(&uc->complete);
+ if (!ret)
+ ucsi_notify_common(uc->ucsi, cci);
return IRQ_HANDLED;
}
@@ -1429,7 +1415,6 @@ static int ucsi_ccg_probe(struct i2c_client *client)
uc->client = client;
uc->irq = client->irq;
mutex_init(&uc->lock);
- init_completion(&uc->complete);
INIT_WORK(&uc->work, ccg_update_firmware);
INIT_WORK(&uc->pm_work, ccg_pm_workaround_work);
@@ -1516,7 +1501,7 @@ static const struct of_device_id ucsi_ccg_of_match_table[] = {
MODULE_DEVICE_TABLE(of, ucsi_ccg_of_match_table);
static const struct i2c_device_id ucsi_ccg_device_id[] = {
- {"ccgx-ucsi", 0},
+ { "ccgx-ucsi" },
{}
};
MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id);
diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c
index 2fa973afe4e6..16c328497e0b 100644
--- a/drivers/usb/typec/ucsi/ucsi_glink.c
+++ b/drivers/usb/typec/ucsi/ucsi_glink.c
@@ -64,12 +64,8 @@ struct pmic_glink_ucsi {
struct ucsi *ucsi;
struct completion read_ack;
struct completion write_ack;
- struct completion sync_ack;
- bool sync_pending;
struct mutex lock; /* protects concurrent access to PMIC Glink interface */
- int sync_val;
-
struct work_struct notify_work;
struct work_struct register_work;
@@ -114,6 +110,21 @@ out_unlock:
return ret;
}
+static int pmic_glink_ucsi_read_version(struct ucsi *ucsi, u16 *version)
+{
+ return pmic_glink_ucsi_read(ucsi, UCSI_VERSION, version, sizeof(*version));
+}
+
+static int pmic_glink_ucsi_read_cci(struct ucsi *ucsi, u32 *cci)
+{
+ return pmic_glink_ucsi_read(ucsi, UCSI_CCI, cci, sizeof(*cci));
+}
+
+static int pmic_glink_ucsi_read_message_in(struct ucsi *ucsi, void *val, size_t val_len)
+{
+ return pmic_glink_ucsi_read(ucsi, UCSI_MESSAGE_IN, val, val_len);
+}
+
static int pmic_glink_ucsi_locked_write(struct pmic_glink_ucsi *ucsi, unsigned int offset,
const void *val, size_t val_len)
{
@@ -143,46 +154,15 @@ static int pmic_glink_ucsi_locked_write(struct pmic_glink_ucsi *ucsi, unsigned i
return 0;
}
-static int pmic_glink_ucsi_async_write(struct ucsi *__ucsi, unsigned int offset,
- const void *val, size_t val_len)
-{
- struct pmic_glink_ucsi *ucsi = ucsi_get_drvdata(__ucsi);
- int ret;
-
- mutex_lock(&ucsi->lock);
- ret = pmic_glink_ucsi_locked_write(ucsi, offset, val, val_len);
- mutex_unlock(&ucsi->lock);
-
- return ret;
-}
-
-static int pmic_glink_ucsi_sync_write(struct ucsi *__ucsi, unsigned int offset,
- const void *val, size_t val_len)
+static int pmic_glink_ucsi_async_control(struct ucsi *__ucsi, u64 command)
{
struct pmic_glink_ucsi *ucsi = ucsi_get_drvdata(__ucsi);
- unsigned long left;
int ret;
- /* TOFIX: Downstream forces recipient to CON when UCSI_GET_ALTERNATE_MODES command */
-
mutex_lock(&ucsi->lock);
- ucsi->sync_val = 0;
- reinit_completion(&ucsi->sync_ack);
- ucsi->sync_pending = true;
- ret = pmic_glink_ucsi_locked_write(ucsi, offset, val, val_len);
+ ret = pmic_glink_ucsi_locked_write(ucsi, UCSI_CONTROL, &command, sizeof(command));
mutex_unlock(&ucsi->lock);
- left = wait_for_completion_timeout(&ucsi->sync_ack, 5 * HZ);
- if (!left) {
- dev_err(ucsi->dev, "timeout waiting for UCSI sync write response\n");
- /* return 0 here and let core UCSI code handle the CCI_BUSY */
- ret = 0;
- } else if (ucsi->sync_val) {
- dev_err(ucsi->dev, "sync write returned: %d\n", ucsi->sync_val);
- }
-
- ucsi->sync_pending = false;
-
return ret;
}
@@ -216,9 +196,11 @@ static void pmic_glink_ucsi_connector_status(struct ucsi_connector *con)
}
static const struct ucsi_operations pmic_glink_ucsi_ops = {
- .read = pmic_glink_ucsi_read,
- .sync_write = pmic_glink_ucsi_sync_write,
- .async_write = pmic_glink_ucsi_async_write,
+ .read_version = pmic_glink_ucsi_read_version,
+ .read_cci = pmic_glink_ucsi_read_cci,
+ .read_message_in = pmic_glink_ucsi_read_message_in,
+ .sync_control = ucsi_sync_control_common,
+ .async_control = pmic_glink_ucsi_async_control,
.update_connector = pmic_glink_ucsi_update_connector,
.connector_status = pmic_glink_ucsi_connector_status,
};
@@ -241,14 +223,12 @@ static void pmic_glink_ucsi_write_ack(struct pmic_glink_ucsi *ucsi, const void *
if (resp->ret_code)
return;
- ucsi->sync_val = resp->ret_code;
complete(&ucsi->write_ack);
}
static void pmic_glink_ucsi_notify(struct work_struct *work)
{
struct pmic_glink_ucsi *ucsi = container_of(work, struct pmic_glink_ucsi, notify_work);
- unsigned int con_num;
u32 cci;
int ret;
@@ -258,14 +238,7 @@ static void pmic_glink_ucsi_notify(struct work_struct *work)
return;
}
- con_num = UCSI_CCI_CONNECTOR(cci);
- if (con_num)
- ucsi_connector_change(ucsi->ucsi, con_num);
-
- if (ucsi->sync_pending &&
- (cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE))) {
- complete(&ucsi->sync_ack);
- }
+ ucsi_notify_common(ucsi->ucsi, cci);
}
static void pmic_glink_ucsi_register(struct work_struct *work)
@@ -347,7 +320,6 @@ static int pmic_glink_ucsi_probe(struct auxiliary_device *adev,
INIT_WORK(&ucsi->register_work, pmic_glink_ucsi_register);
init_completion(&ucsi->read_ack);
init_completion(&ucsi->write_ack);
- init_completion(&ucsi->sync_ack);
mutex_init(&ucsi->lock);
ucsi->ucsi = ucsi_create(dev, &pmic_glink_ucsi_ops);
diff --git a/drivers/usb/typec/ucsi/ucsi_stm32g0.c b/drivers/usb/typec/ucsi/ucsi_stm32g0.c
index ac69288e8bb0..ddbec2b78c8e 100644
--- a/drivers/usb/typec/ucsi/ucsi_stm32g0.c
+++ b/drivers/usb/typec/ucsi/ucsi_stm32g0.c
@@ -61,11 +61,7 @@ struct ucsi_stm32g0 {
struct i2c_client *i2c_bl;
bool in_bootloader;
u8 bl_version;
- struct completion complete;
struct device *dev;
- unsigned long flags;
-#define COMMAND_PENDING 1
-#define ACK_PENDING 2
const char *fw_name;
struct ucsi *ucsi;
bool suspended;
@@ -359,8 +355,22 @@ static int ucsi_stm32g0_read(struct ucsi *ucsi, unsigned int offset, void *val,
return 0;
}
-static int ucsi_stm32g0_async_write(struct ucsi *ucsi, unsigned int offset, const void *val,
- size_t len)
+static int ucsi_stm32g0_read_version(struct ucsi *ucsi, u16 *version)
+{
+ return ucsi_stm32g0_read(ucsi, UCSI_VERSION, version, sizeof(*version));
+}
+
+static int ucsi_stm32g0_read_cci(struct ucsi *ucsi, u32 *cci)
+{
+ return ucsi_stm32g0_read(ucsi, UCSI_CCI, cci, sizeof(*cci));
+}
+
+static int ucsi_stm32g0_read_message_in(struct ucsi *ucsi, void *val, size_t len)
+{
+ return ucsi_stm32g0_read(ucsi, UCSI_MESSAGE_IN, val, len);
+}
+
+static int ucsi_stm32g0_async_control(struct ucsi *ucsi, u64 command)
{
struct ucsi_stm32g0 *g0 = ucsi_get_drvdata(ucsi);
struct i2c_client *client = g0->client;
@@ -373,19 +383,19 @@ static int ucsi_stm32g0_async_write(struct ucsi *ucsi, unsigned int offset, cons
unsigned char *buf;
int ret;
- buf = kmalloc(len + 1, GFP_KERNEL);
+ buf = kmalloc(sizeof(command) + 1, GFP_KERNEL);
if (!buf)
return -ENOMEM;
- buf[0] = offset;
- memcpy(&buf[1], val, len);
- msg[0].len = len + 1;
+ buf[0] = UCSI_CONTROL;
+ memcpy(&buf[1], &command, sizeof(command));
+ msg[0].len = sizeof(command) + 1;
msg[0].buf = buf;
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
kfree(buf);
if (ret != ARRAY_SIZE(msg)) {
- dev_err(g0->dev, "i2c write %02x, %02x error: %d\n", client->addr, offset, ret);
+ dev_err(g0->dev, "i2c write %02x, %02x error: %d\n", client->addr, UCSI_CONTROL, ret);
return ret < 0 ? ret : -EIO;
}
@@ -393,36 +403,6 @@ static int ucsi_stm32g0_async_write(struct ucsi *ucsi, unsigned int offset, cons
return 0;
}
-static int ucsi_stm32g0_sync_write(struct ucsi *ucsi, unsigned int offset, const void *val,
- size_t len)
-{
- struct ucsi_stm32g0 *g0 = ucsi_get_drvdata(ucsi);
- bool ack = UCSI_COMMAND(*(u64 *)val) == UCSI_ACK_CC_CI;
- int ret;
-
- if (ack)
- set_bit(ACK_PENDING, &g0->flags);
- else
- set_bit(COMMAND_PENDING, &g0->flags);
-
- ret = ucsi_stm32g0_async_write(ucsi, offset, val, len);
- if (ret)
- goto out_clear_bit;
-
- if (!wait_for_completion_timeout(&g0->complete, msecs_to_jiffies(5000)))
- ret = -ETIMEDOUT;
- else
- return 0;
-
-out_clear_bit:
- if (ack)
- clear_bit(ACK_PENDING, &g0->flags);
- else
- clear_bit(COMMAND_PENDING, &g0->flags);
-
- return ret;
-}
-
static irqreturn_t ucsi_stm32g0_irq_handler(int irq, void *data)
{
struct ucsi_stm32g0 *g0 = data;
@@ -436,21 +416,17 @@ static irqreturn_t ucsi_stm32g0_irq_handler(int irq, void *data)
if (ret)
return IRQ_NONE;
- if (UCSI_CCI_CONNECTOR(cci))
- ucsi_connector_change(g0->ucsi, UCSI_CCI_CONNECTOR(cci));
-
- if (cci & UCSI_CCI_ACK_COMPLETE && test_and_clear_bit(ACK_PENDING, &g0->flags))
- complete(&g0->complete);
- if (cci & UCSI_CCI_COMMAND_COMPLETE && test_and_clear_bit(COMMAND_PENDING, &g0->flags))
- complete(&g0->complete);
+ ucsi_notify_common(g0->ucsi, cci);
return IRQ_HANDLED;
}
static const struct ucsi_operations ucsi_stm32g0_ops = {
- .read = ucsi_stm32g0_read,
- .sync_write = ucsi_stm32g0_sync_write,
- .async_write = ucsi_stm32g0_async_write,
+ .read_version = ucsi_stm32g0_read_version,
+ .read_cci = ucsi_stm32g0_read_cci,
+ .read_message_in = ucsi_stm32g0_read_message_in,
+ .sync_control = ucsi_sync_control_common,
+ .async_control = ucsi_stm32g0_async_control,
};
static int ucsi_stm32g0_register(struct ucsi *ucsi)
@@ -650,7 +626,6 @@ static int ucsi_stm32g0_probe(struct i2c_client *client)
g0->dev = dev;
g0->client = client;
- init_completion(&g0->complete);
i2c_set_clientdata(client, g0);
g0->ucsi = ucsi_create(dev, &ucsi_stm32g0_ops);
@@ -764,8 +739,8 @@ static const struct of_device_id __maybe_unused ucsi_stm32g0_typec_of_match[] =
MODULE_DEVICE_TABLE(of, ucsi_stm32g0_typec_of_match);
static const struct i2c_device_id ucsi_stm32g0_typec_i2c_devid[] = {
- {"stm32g0-typec", 0},
- {},
+ { "stm32g0-typec" },
+ {}
};
MODULE_DEVICE_TABLE(i2c, ucsi_stm32g0_typec_i2c_devid);
diff --git a/drivers/usb/typec/ucsi/ucsi_yoga_c630.c b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
new file mode 100644
index 000000000000..f3a5e24ea84d
--- /dev/null
+++ b/drivers/usb/typec/ucsi/ucsi_yoga_c630.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2022-2024, Linaro Ltd
+ * Authors:
+ * Bjorn Andersson
+ * Dmitry Baryshkov
+ */
+#include <linux/auxiliary_bus.h>
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/container_of.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/string.h>
+#include <linux/platform_data/lenovo-yoga-c630.h>
+
+#include "ucsi.h"
+
+struct yoga_c630_ucsi {
+ struct yoga_c630_ec *ec;
+ struct ucsi *ucsi;
+ struct notifier_block nb;
+ u16 version;
+};
+
+static int yoga_c630_ucsi_read_version(struct ucsi *ucsi, u16 *version)
+{
+ struct yoga_c630_ucsi *uec = ucsi_get_drvdata(ucsi);
+
+ *version = uec->version;
+
+ return 0;
+}
+
+static int yoga_c630_ucsi_read_cci(struct ucsi *ucsi, u32 *cci)
+{
+ struct yoga_c630_ucsi *uec = ucsi_get_drvdata(ucsi);
+ u8 buf[YOGA_C630_UCSI_READ_SIZE];
+ int ret;
+
+ ret = yoga_c630_ec_ucsi_read(uec->ec, buf);
+ if (ret)
+ return ret;
+
+ memcpy(cci, buf, sizeof(*cci));
+
+ return 0;
+}
+
+static int yoga_c630_ucsi_read_message_in(struct ucsi *ucsi,
+ void *val, size_t val_len)
+{
+ struct yoga_c630_ucsi *uec = ucsi_get_drvdata(ucsi);
+ u8 buf[YOGA_C630_UCSI_READ_SIZE];
+ int ret;
+
+ ret = yoga_c630_ec_ucsi_read(uec->ec, buf);
+ if (ret)
+ return ret;
+
+ memcpy(val, buf + YOGA_C630_UCSI_CCI_SIZE,
+ min(val_len, YOGA_C630_UCSI_DATA_SIZE));
+
+ return 0;
+}
+
+static int yoga_c630_ucsi_async_control(struct ucsi *ucsi, u64 command)
+{
+ struct yoga_c630_ucsi *uec = ucsi_get_drvdata(ucsi);
+
+ return yoga_c630_ec_ucsi_write(uec->ec, (u8*)&command);
+}
+
+const struct ucsi_operations yoga_c630_ucsi_ops = {
+ .read_version = yoga_c630_ucsi_read_version,
+ .read_cci = yoga_c630_ucsi_read_cci,
+ .read_message_in = yoga_c630_ucsi_read_message_in,
+ .sync_control = ucsi_sync_control_common,
+ .async_control = yoga_c630_ucsi_async_control,
+};
+
+static int yoga_c630_ucsi_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct yoga_c630_ucsi *uec = container_of(nb, struct yoga_c630_ucsi, nb);
+ u32 cci;
+ int ret;
+
+ switch (action) {
+ case LENOVO_EC_EVENT_USB:
+ case LENOVO_EC_EVENT_HPD:
+ ucsi_connector_change(uec->ucsi, 1);
+ return NOTIFY_OK;
+
+ case LENOVO_EC_EVENT_UCSI:
+ ret = uec->ucsi->ops->read_cci(uec->ucsi, &cci);
+ if (ret)
+ return NOTIFY_DONE;
+
+ ucsi_notify_common(uec->ucsi, cci);
+
+ return NOTIFY_OK;
+
+ default:
+ return NOTIFY_DONE;
+ }
+}
+
+static int yoga_c630_ucsi_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct yoga_c630_ec *ec = adev->dev.platform_data;
+ struct yoga_c630_ucsi *uec;
+ int ret;
+
+ uec = devm_kzalloc(&adev->dev, sizeof(*uec), GFP_KERNEL);
+ if (!uec)
+ return -ENOMEM;
+
+ uec->ec = ec;
+ uec->nb.notifier_call = yoga_c630_ucsi_notify;
+
+ uec->ucsi = ucsi_create(&adev->dev, &yoga_c630_ucsi_ops);
+ if (IS_ERR(uec->ucsi))
+ return PTR_ERR(uec->ucsi);
+
+ ucsi_set_drvdata(uec->ucsi, uec);
+
+ uec->version = yoga_c630_ec_ucsi_get_version(uec->ec);
+
+ auxiliary_set_drvdata(adev, uec);
+
+ ret = yoga_c630_ec_register_notify(ec, &uec->nb);
+ if (ret)
+ return ret;
+
+ return ucsi_register(uec->ucsi);
+}
+
+static void yoga_c630_ucsi_remove(struct auxiliary_device *adev)
+{
+ struct yoga_c630_ucsi *uec = auxiliary_get_drvdata(adev);
+
+ yoga_c630_ec_unregister_notify(uec->ec, &uec->nb);
+ ucsi_unregister(uec->ucsi);
+}
+
+static const struct auxiliary_device_id yoga_c630_ucsi_id_table[] = {
+ { .name = YOGA_C630_MOD_NAME "." YOGA_C630_DEV_UCSI, },
+ {}
+};
+MODULE_DEVICE_TABLE(auxiliary, yoga_c630_ucsi_id_table);
+
+static struct auxiliary_driver yoga_c630_ucsi_driver = {
+ .name = YOGA_C630_DEV_UCSI,
+ .id_table = yoga_c630_ucsi_id_table,
+ .probe = yoga_c630_ucsi_probe,
+ .remove = yoga_c630_ucsi_remove,
+};
+
+module_auxiliary_driver(yoga_c630_ucsi_driver);
+
+MODULE_DESCRIPTION("Lenovo Yoga C630 UCSI");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/usbip/stub_rx.c b/drivers/usb/usbip/stub_rx.c
index fc01b31bbb87..6338d818bc8b 100644
--- a/drivers/usb/usbip/stub_rx.c
+++ b/drivers/usb/usbip/stub_rx.c
@@ -144,53 +144,62 @@ static int tweak_set_configuration_cmd(struct urb *urb)
if (err && err != -ENODEV)
dev_err(&sdev->udev->dev, "can't set config #%d, error %d\n",
config, err);
- return 0;
+ return err;
}
static int tweak_reset_device_cmd(struct urb *urb)
{
struct stub_priv *priv = (struct stub_priv *) urb->context;
struct stub_device *sdev = priv->sdev;
+ int err;
dev_info(&urb->dev->dev, "usb_queue_reset_device\n");
- if (usb_lock_device_for_reset(sdev->udev, NULL) < 0) {
+ err = usb_lock_device_for_reset(sdev->udev, NULL);
+ if (err < 0) {
dev_err(&urb->dev->dev, "could not obtain lock to reset device\n");
- return 0;
+ return err;
}
- usb_reset_device(sdev->udev);
+ err = usb_reset_device(sdev->udev);
usb_unlock_device(sdev->udev);
- return 0;
+ return err;
}
/*
* clear_halt, set_interface, and set_configuration require special tricks.
+ * Returns 1 if request was tweaked, 0 otherwise.
*/
-static void tweak_special_requests(struct urb *urb)
+static int tweak_special_requests(struct urb *urb)
{
+ int err;
+
if (!urb || !urb->setup_packet)
- return;
+ return 0;
if (usb_pipetype(urb->pipe) != PIPE_CONTROL)
- return;
+ return 0;
if (is_clear_halt_cmd(urb))
/* tweak clear_halt */
- tweak_clear_halt_cmd(urb);
+ err = tweak_clear_halt_cmd(urb);
else if (is_set_interface_cmd(urb))
/* tweak set_interface */
- tweak_set_interface_cmd(urb);
+ err = tweak_set_interface_cmd(urb);
else if (is_set_configuration_cmd(urb))
/* tweak set_configuration */
- tweak_set_configuration_cmd(urb);
+ err = tweak_set_configuration_cmd(urb);
else if (is_reset_device_cmd(urb))
- tweak_reset_device_cmd(urb);
- else
+ err = tweak_reset_device_cmd(urb);
+ else {
usbip_dbg_stub_rx("no need to tweak\n");
+ return 0;
+ }
+
+ return !err;
}
/*
@@ -468,6 +477,7 @@ static void stub_recv_cmd_submit(struct stub_device *sdev,
int support_sg = 1;
int np = 0;
int ret, i;
+ int is_tweaked;
if (pipe == -1)
return;
@@ -580,8 +590,11 @@ static void stub_recv_cmd_submit(struct stub_device *sdev,
priv->urbs[i]->pipe = pipe;
priv->urbs[i]->complete = stub_complete;
- /* no need to submit an intercepted request, but harmless? */
- tweak_special_requests(priv->urbs[i]);
+ /*
+ * all URBs belong to a single PDU, so a global is_tweaked flag is
+ * enough
+ */
+ is_tweaked = tweak_special_requests(priv->urbs[i]);
masking_bogus_flags(priv->urbs[i]);
}
@@ -594,22 +607,32 @@ static void stub_recv_cmd_submit(struct stub_device *sdev,
/* urb is now ready to submit */
for (i = 0; i < priv->num_urbs; i++) {
- ret = usb_submit_urb(priv->urbs[i], GFP_KERNEL);
+ if (!is_tweaked) {
+ ret = usb_submit_urb(priv->urbs[i], GFP_KERNEL);
- if (ret == 0)
- usbip_dbg_stub_rx("submit urb ok, seqnum %u\n",
- pdu->base.seqnum);
- else {
- dev_err(&udev->dev, "submit_urb error, %d\n", ret);
- usbip_dump_header(pdu);
- usbip_dump_urb(priv->urbs[i]);
+ if (ret == 0)
+ usbip_dbg_stub_rx("submit urb ok, seqnum %u\n",
+ pdu->base.seqnum);
+ else {
+ dev_err(&udev->dev, "submit_urb error, %d\n", ret);
+ usbip_dump_header(pdu);
+ usbip_dump_urb(priv->urbs[i]);
+ /*
+ * Pessimistic.
+ * This connection will be discarded.
+ */
+ usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT);
+ break;
+ }
+ } else {
/*
- * Pessimistic.
- * This connection will be discarded.
+ * An identical URB was already submitted in
+ * tweak_special_requests(). Skip submitting this URB to not
+ * duplicate the request.
*/
- usbip_event_add(ud, SDEV_EVENT_ERROR_SUBMIT);
- break;
+ priv->urbs[i]->status = 0;
+ stub_complete(priv->urbs[i]);
}
}