diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-07-19 15:37:48 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-07-19 15:37:48 -0700 |
commit | 04d17331ca33744e1426fdeee7ba5e975c4b2239 (patch) | |
tree | 970a241cfd378f097c67d29bdaaaa6bf6e6c3413 /drivers/usb | |
parent | aba9753c0677e860f982edff98c7fe5a2b97758c (diff) | |
parent | b727493011123db329e2901e3abf81a8d146b6fe (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')
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, ®); + 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]); } } |