From 986866c3dfb0f930c5cc109a6c509c1391d96b5f Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 24 Apr 2023 10:39:56 +0200 Subject: usb: typec: mux: Remove some unneeded includes This driver includes many header files that are unneeded. Remove them and add where devm_kzalloc() is defined. Signed-off-by: Christophe JAILLET Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/1db1e8bd253cbb652835c0cef6a0a2bb9a4970eb.1682325582.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/gpio-sbu-mux.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/usb/typec/mux/gpio-sbu-mux.c b/drivers/usb/typec/mux/gpio-sbu-mux.c index f62516dafe8f..c07856069d43 100644 --- a/drivers/usb/typec/mux/gpio-sbu-mux.c +++ b/drivers/usb/typec/mux/gpio-sbu-mux.c @@ -3,14 +3,11 @@ * Copyright (C) 2022 Linaro Ltd. */ -#include -#include -#include +#include #include #include #include #include -#include #include #include -- cgit v1.2.3 From 1f7d5520719dd1fed1a2947679f6cc26a55f1e6b Mon Sep 17 00:00:00 2001 From: Basavaraj Natikar Date: Fri, 28 Apr 2023 19:30:55 +0530 Subject: USB: Extend pci resume function to handle PM events Currently, the pci_resume method has only a flag indicating whether the system is resuming from hibernation. In order to handle all PM events like AUTO_RESUME (runtime resume from device in D3), RESUME (system resume from s2idle, S3 or S4 states) etc change the pci_resume method to handle all PM events. Signed-off-by: Basavaraj Natikar Acked-by: Alan Stern Acked-by: Mathias Nyman Link: https://lore.kernel.org/r/20230428140056.1318981-2-Basavaraj.Natikar@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd-pci.c | 24 +++++++++++++----------- drivers/usb/host/ehci-pci.c | 3 ++- drivers/usb/host/ohci-pci.c | 8 +++++++- drivers/usb/host/uhci-pci.c | 7 ++++--- drivers/usb/host/xhci-histb.c | 2 +- drivers/usb/host/xhci-pci.c | 4 ++-- drivers/usb/host/xhci-plat.c | 4 ++-- drivers/usb/host/xhci-tegra.c | 2 +- drivers/usb/host/xhci.c | 3 ++- drivers/usb/host/xhci.h | 2 +- include/linux/usb/hcd.h | 2 +- 11 files changed, 36 insertions(+), 25 deletions(-) diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index ab2f3737764e..990280688b25 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -415,12 +415,15 @@ static int check_root_hub_suspended(struct device *dev) return 0; } -static int suspend_common(struct device *dev, bool do_wakeup) +static int suspend_common(struct device *dev, pm_message_t msg) { struct pci_dev *pci_dev = to_pci_dev(dev); struct usb_hcd *hcd = pci_get_drvdata(pci_dev); + bool do_wakeup; int retval; + do_wakeup = PMSG_IS_AUTO(msg) ? true : device_may_wakeup(dev); + /* Root hub suspend should have stopped all downstream traffic, * and all bus master traffic. And done so for both the interface * and the stub usb_device (which we check here). But maybe it @@ -447,7 +450,7 @@ static int suspend_common(struct device *dev, bool do_wakeup) (retval == 0 && do_wakeup && hcd->shared_hcd && HCD_WAKEUP_PENDING(hcd->shared_hcd))) { if (hcd->driver->pci_resume) - hcd->driver->pci_resume(hcd, false); + hcd->driver->pci_resume(hcd, msg); retval = -EBUSY; } if (retval) @@ -470,7 +473,7 @@ static int suspend_common(struct device *dev, bool do_wakeup) return retval; } -static int resume_common(struct device *dev, int event) +static int resume_common(struct device *dev, pm_message_t msg) { struct pci_dev *pci_dev = to_pci_dev(dev); struct usb_hcd *hcd = pci_get_drvdata(pci_dev); @@ -498,12 +501,11 @@ static int resume_common(struct device *dev, int event) * No locking is needed because PCI controller drivers do not * get unbound during system resume. */ - if (pci_dev->class == CL_EHCI && event != PM_EVENT_AUTO_RESUME) + if (pci_dev->class == CL_EHCI && msg.event != PM_EVENT_AUTO_RESUME) for_each_companion(pci_dev, hcd, ehci_wait_for_companions); - retval = hcd->driver->pci_resume(hcd, - event == PM_EVENT_RESTORE); + retval = hcd->driver->pci_resume(hcd, msg); if (retval) { dev_err(dev, "PCI post-resume error %d!\n", retval); usb_hc_died(hcd); @@ -516,7 +518,7 @@ static int resume_common(struct device *dev, int event) static int hcd_pci_suspend(struct device *dev) { - return suspend_common(dev, device_may_wakeup(dev)); + return suspend_common(dev, PMSG_SUSPEND); } static int hcd_pci_suspend_noirq(struct device *dev) @@ -577,12 +579,12 @@ static int hcd_pci_resume_noirq(struct device *dev) static int hcd_pci_resume(struct device *dev) { - return resume_common(dev, PM_EVENT_RESUME); + return resume_common(dev, PMSG_RESUME); } static int hcd_pci_restore(struct device *dev) { - return resume_common(dev, PM_EVENT_RESTORE); + return resume_common(dev, PMSG_RESTORE); } #else @@ -600,7 +602,7 @@ static int hcd_pci_runtime_suspend(struct device *dev) { int retval; - retval = suspend_common(dev, true); + retval = suspend_common(dev, PMSG_AUTO_SUSPEND); if (retval == 0) powermac_set_asic(to_pci_dev(dev), 0); dev_dbg(dev, "hcd_pci_runtime_suspend: %d\n", retval); @@ -612,7 +614,7 @@ static int hcd_pci_runtime_resume(struct device *dev) int retval; powermac_set_asic(to_pci_dev(dev), 1); - retval = resume_common(dev, PM_EVENT_AUTO_RESUME); + retval = resume_common(dev, PMSG_AUTO_RESUME); dev_dbg(dev, "hcd_pci_runtime_resume: %d\n", retval); return retval; } diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 4b148fe5e43b..889dc4426271 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -354,10 +354,11 @@ done: * Also they depend on separate root hub suspend/resume. */ -static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated) +static int ehci_pci_resume(struct usb_hcd *hcd, pm_message_t msg) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + bool hibernated = (msg.event == PM_EVENT_RESTORE); if (ehci_resume(hcd, hibernated) != 0) (void) ehci_pci_reinit(ehci, pdev); diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index d7b4f40f9ff4..900ea0d368e0 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -301,6 +301,12 @@ static struct pci_driver ohci_pci_driver = { #endif }; +#ifdef CONFIG_PM +static int ohci_pci_resume(struct usb_hcd *hcd, pm_message_t msg) +{ + return ohci_resume(hcd, msg.event == PM_EVENT_RESTORE); +} +#endif static int __init ohci_pci_init(void) { if (usb_disabled()) @@ -311,7 +317,7 @@ static int __init ohci_pci_init(void) #ifdef CONFIG_PM /* Entries for the PCI suspend/resume callbacks are special */ ohci_pci_hc_driver.pci_suspend = ohci_suspend; - ohci_pci_hc_driver.pci_resume = ohci_resume; + ohci_pci_hc_driver.pci_resume = ohci_pci_resume; #endif return pci_register_driver(&ohci_pci_driver); diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c index 3592f757fe05..5df4a1832b09 100644 --- a/drivers/usb/host/uhci-pci.c +++ b/drivers/usb/host/uhci-pci.c @@ -167,7 +167,7 @@ static void uhci_shutdown(struct pci_dev *pdev) #ifdef CONFIG_PM -static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated); +static int uhci_pci_resume(struct usb_hcd *hcd, pm_message_t state); static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) { @@ -202,14 +202,15 @@ done_okay: /* Check for race with a wakeup request */ if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) { - uhci_pci_resume(hcd, false); + uhci_pci_resume(hcd, PMSG_SUSPEND); rc = -EBUSY; } return rc; } -static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated) +static int uhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg) { + bool hibernated = (msg.event == PM_EVENT_RESTORE); struct uhci_hcd *uhci = hcd_to_uhci(hcd); dev_dbg(uhci_dev(uhci), "%s\n", __func__); diff --git a/drivers/usb/host/xhci-histb.c b/drivers/usb/host/xhci-histb.c index 08369857686e..91ce97821de5 100644 --- a/drivers/usb/host/xhci-histb.c +++ b/drivers/usb/host/xhci-histb.c @@ -367,7 +367,7 @@ static int __maybe_unused xhci_histb_resume(struct device *dev) if (!device_may_wakeup(dev)) xhci_histb_host_enable(histb); - return xhci_resume(xhci, 0); + return xhci_resume(xhci, PMSG_RESUME); } static const struct dev_pm_ops xhci_histb_pm_ops = { diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index ddb79f23fb3b..e834c77e188e 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -824,7 +824,7 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) return ret; } -static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) +static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct pci_dev *pdev = to_pci_dev(hcd->self.controller); @@ -859,7 +859,7 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) if (xhci->quirks & XHCI_PME_STUCK_QUIRK) xhci_pme_quirk(hcd); - retval = xhci_resume(xhci, hibernated); + retval = xhci_resume(xhci, msg); return retval; } diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index b0c8e8efc43b..f36633fa8362 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -478,7 +478,7 @@ static int __maybe_unused xhci_plat_resume(struct device *dev) if (ret) return ret; - ret = xhci_resume(xhci, 0); + ret = xhci_resume(xhci, PMSG_RESUME); if (ret) return ret; @@ -507,7 +507,7 @@ static int __maybe_unused xhci_plat_runtime_resume(struct device *dev) struct usb_hcd *hcd = dev_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(hcd); - return xhci_resume(xhci, 0); + return xhci_resume(xhci, PMSG_AUTO_RESUME); } const struct dev_pm_ops xhci_plat_pm_ops = { diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index c75d93244143..8a9c7deb7686 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -2272,7 +2272,7 @@ static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime) if (wakeup) tegra_xhci_disable_phy_sleepwalk(tegra); - err = xhci_resume(xhci, 0); + err = xhci_resume(xhci, runtime ? PMSG_AUTO_RESUME : PMSG_RESUME); if (err < 0) { dev_err(tegra->dev, "failed to resume XHCI: %d\n", err); goto disable_phy; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 78790dc13c5f..65d54c8a2492 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -960,8 +960,9 @@ EXPORT_SYMBOL_GPL(xhci_suspend); * This is called when the machine transition from S3/S4 mode. * */ -int xhci_resume(struct xhci_hcd *xhci, bool hibernated) +int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg) { + bool hibernated = (msg.event == PM_EVENT_RESTORE); u32 command, temp = 0; struct usb_hcd *hcd = xhci_to_hcd(xhci); int retval = 0; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 08d721921b7b..047b290404b4 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -2140,7 +2140,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id); int xhci_ext_cap_init(struct xhci_hcd *xhci); int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup); -int xhci_resume(struct xhci_hcd *xhci, bool hibernated); +int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg); irqreturn_t xhci_irq(struct usb_hcd *hcd); irqreturn_t xhci_msi_irq(int irq, void *hcd); diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 094c77eaf455..30ab8994f0c1 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -267,7 +267,7 @@ struct hc_driver { int (*pci_suspend)(struct usb_hcd *hcd, bool do_wakeup); /* called after entering D0 (etc), before resuming the hub */ - int (*pci_resume)(struct usb_hcd *hcd, bool hibernated); + int (*pci_resume)(struct usb_hcd *hcd, pm_message_t state); /* called just before hibernate final D3 state, allows host to poweroff parts */ int (*pci_poweroff_late)(struct usb_hcd *hcd, bool do_wakeup); -- cgit v1.2.3 From 1c024241d018cf9fc17aa8d95c3fe77d671d7142 Mon Sep 17 00:00:00 2001 From: Basavaraj Natikar Date: Fri, 28 Apr 2023 19:30:56 +0530 Subject: xhci: Improve the XHCI system resume time Avoid extra 120ms delay during system resume. The xHC controller may signal wake up to 120ms before showing which usb device caused the wake on the xHC port registers. The xhci driver therefore checks for port activity up to 120ms during resume, making sure that the hub driver can see the port change, and won't immediately runtime suspend back due to no port activity. This is however only needed for runtime resume as system resume will resume all child hubs and other child usb devices anyway. Fixes: 253f588c70f6 ("xhci: Improve detection of device initiated wake signal.") Signed-off-by: Basavaraj Natikar Acked-by: Mathias Nyman Link: https://lore.kernel.org/r/20230428140056.1318981-3-Basavaraj.Natikar@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 65d54c8a2492..b81313ffeb76 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1117,7 +1117,7 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg) * the first wake signalling failed, give it that chance. */ pending_portevent = xhci_pending_portevent(xhci); - if (!pending_portevent) { + if (!pending_portevent && msg.event == PM_EVENT_AUTO_RESUME) { msleep(120); pending_portevent = xhci_pending_portevent(xhci); } -- cgit v1.2.3 From ec5eb43813a4c775b5abf20f50461037bca7c4e5 Mon Sep 17 00:00:00 2001 From: Stanley Chang Date: Fri, 5 May 2023 10:50:54 +0800 Subject: usb: dwc3: core: add support for realtek SoCs custom's global register start address The Realtek RTD SoCs were designed with the global register address offset at 0x8100. The default address offset is constant at DWC3_GLOBALS_REGS_START (0xc100). Therefore, add a check if the compatible name of the parent is realtek,rtd-dwc3, then global register start address will remap to 0x8100. Signed-off-by: Stanley Chang Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20230505025104.18321-1-stanley_chang@realtek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 11 +++++++++++ drivers/usb/dwc3/core.h | 2 ++ 2 files changed, 13 insertions(+) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 0beaab932e7d..278cd1c33841 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1800,6 +1800,17 @@ static int dwc3_probe(struct platform_device *pdev) dwc_res = *res; dwc_res.start += DWC3_GLOBALS_REGS_START; + if (dev->of_node) { + struct device_node *parent = of_get_parent(dev->of_node); + + if (of_device_is_compatible(parent, "realtek,rtd-dwc3")) { + dwc_res.start -= DWC3_GLOBALS_REGS_START; + dwc_res.start += DWC3_RTK_RTD_GLOBALS_REGS_START; + } + + of_node_put(parent); + } + regs = devm_ioremap_resource(dev, &dwc_res); if (IS_ERR(regs)) return PTR_ERR(regs); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index d56457c02996..1968638f29ed 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -84,6 +84,8 @@ #define DWC3_OTG_REGS_START 0xcc00 #define DWC3_OTG_REGS_END 0xccff +#define DWC3_RTK_RTD_GLOBALS_REGS_START 0x8100 + /* Global Registers */ #define DWC3_GSBUSCFG0 0xc100 #define DWC3_GSBUSCFG1 0xc104 -- cgit v1.2.3 From 424e02931e2bad6f8c442fd3d548379123bf1b13 Mon Sep 17 00:00:00 2001 From: Stanley Chang Date: Wed, 10 May 2023 15:51:29 +0800 Subject: usb: xhci: plat: remove error log for failure to get usb-phy Remove this log to avoid non-error conditions. If CONFIG_USB_PHY is disabled, the following error message appears: [ 0.231609] xhci-hcd f10f0000.usb3: xhci_plat_probe get usb3phy fail (ret=-6) [ 0.239716] xhci-hcd f10f8000.usb3: xhci_plat_probe get usb3phy fail (ret=-6) In this case, devm_usb_get_phy_by_phandle is declared static inline and returns -ENXIO. It is easy to pinpoint the failure to get the usb-phy using the debug log in drivers/usb/phy/phy.c. Therefore, it can be removed. Signed-off-by: Stanley Chang Tested-by: Klaus Kudielka Link: https://lore.kernel.org/r/20230510075129.28047-1-stanley_chang@realtek.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-plat.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index f36633fa8362..a666b21c21bb 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -294,10 +294,6 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s xhci->shared_hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 1); if (IS_ERR(xhci->shared_hcd->usb_phy)) { - if (PTR_ERR(xhci->shared_hcd->usb_phy) != -ENODEV) - dev_err(sysdev, "%s get usb3phy fail (ret=%d)\n", - __func__, - (int)PTR_ERR(xhci->shared_hcd->usb_phy)); xhci->shared_hcd->usb_phy = NULL; } else { ret = usb_phy_init(xhci->shared_hcd->usb_phy); -- cgit v1.2.3 From 397376765249555800749d012b301609c62ae963 Mon Sep 17 00:00:00 2001 From: Henry Lin Date: Fri, 12 May 2023 16:04:23 +0800 Subject: usb: xhci: tegra: enable stream protocol support This commit enables stream transfer protocol for Tegra XHCI. Signed-off-by: Henry Lin Signed-off-by: Jim Lin Link: https://lore.kernel.org/r/20230512080423.27978-1-jilin@nvidia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 8a9c7deb7686..393e2c8064bd 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1828,6 +1828,9 @@ static int tegra_xusb_probe(struct platform_device *pdev) goto remove_usb2; } + if (HCC_MAX_PSA(xhci->hcc_params) >= 4) + xhci->shared_hcd->can_do_streams = 1; + err = usb_add_hcd(xhci->shared_hcd, tegra->xhci_irq, IRQF_SHARED); if (err < 0) { dev_err(&pdev->dev, "failed to add shared HCD: %d\n", err); -- cgit v1.2.3 From 02be19e914b8ec5b4e355508daf640dde761d300 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Fri, 12 May 2023 14:39:41 +0200 Subject: dt-bindings: usb: Add support for Microchip usb5744 hub controller The Microchip usb5744 is a SS/HS USB 3.0 hub controller with 4 ports. Add description for USB related aspects of the USB5744 hub, it as well cover the option of connecting the controller as an i2c slave. When i2c interface is connected hub needs to be initialized first. Hub itself has fixed i2c address 0x2D but hardcoding address is not good idea because address can be shifted by i2c address translator in the middle. Signed-off-by: Piyush Mehta Signed-off-by: Michal Simek Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/067fb163bfe3162c596a6c69c96c43ac78288628.1683895176.git.michal.simek@amd.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/microchip,usb5744.yaml | 107 +++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/microchip,usb5744.yaml diff --git a/Documentation/devicetree/bindings/usb/microchip,usb5744.yaml b/Documentation/devicetree/bindings/usb/microchip,usb5744.yaml new file mode 100644 index 000000000000..ff3a1707ef57 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/microchip,usb5744.yaml @@ -0,0 +1,107 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/microchip,usb5744.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip USB5744 4-port Hub Controller + +description: + Microchip's USB5744 SmartHubTM IC is a 4 port, SuperSpeed (SS)/Hi-Speed (HS), + low power, low pin count configurable and fully compliant with the USB 3.1 + Gen 1 specification. The USB5744 also supports Full Speed (FS) and Low Speed + (LS) USB signaling, offering complete coverage of all defined USB operating + speeds. The new SuperSpeed hubs operate in parallel with the USB 2.0 + controller, so 5 Gbps SuperSpeed data transfers are not affected by slower + USB 2.0 traffic. + +maintainers: + - Piyush Mehta + - Michal Simek + +properties: + compatible: + enum: + - usb424,2744 + - usb424,5744 + - microchip,usb5744 + + reg: + maxItems: 1 + + reset-gpios: + maxItems: 1 + description: + GPIO controlling the GRST# pin. + + vdd-supply: + description: + VDD power supply to the hub + + peer-hub: + $ref: /schemas/types.yaml#/definitions/phandle + description: + phandle to the peer hub on the controller. + + i2c-bus: + $ref: /schemas/types.yaml#/definitions/phandle + description: + phandle of an usb hub connected via i2c bus. + +required: + - compatible + - reg + +allOf: + - if: + properties: + compatible: + contains: + const: microchip,usb5744 + then: + properties: + reset-gpios: false + vdd-supply: false + peer-hub: false + i2c-bus: false + else: + $ref: /schemas/usb/usb-device.yaml + required: + - peer-hub + +additionalProperties: false + +examples: + - | + #include + i2c: i2c { + #address-cells = <1>; + #size-cells = <0>; + hub: usb-hub@2d { + compatible = "microchip,usb5744"; + reg = <0x2d>; + }; + }; + + usb { + #address-cells = <1>; + #size-cells = <0>; + + /* 2.0 hub on port 1 */ + hub_2_0: hub@1 { + compatible = "usb424,2744"; + reg = <1>; + peer-hub = <&hub_3_0>; + i2c-bus = <&hub>; + reset-gpios = <&gpio 3 GPIO_ACTIVE_LOW>; + }; + + /* 3.0 hub on port 2 */ + hub_3_0: hub@2 { + compatible = "usb424,5744"; + reg = <2>; + peer-hub = <&hub_2_0>; + i2c-bus = <&hub>; + reset-gpios = <&gpio 3 GPIO_ACTIVE_LOW>; + }; + }; -- cgit v1.2.3 From 2f28c3c9c34742d501b623acf8ebfe3a7faed8a3 Mon Sep 17 00:00:00 2001 From: Roy Luo Date: Thu, 4 May 2023 00:01:29 +0000 Subject: usb: dwc3: Add error logs for unknown endpoint events In cases where the controller somehow fails to write to event buffer memory (e.g. due to incorrect MMU config), the driver would receive all-zero dwc3 events. However, the abnormal event is silently dropped as a regular ep0out event. Add error logs when an unknown endpoint event is received to highlight the anomaly. Signed-off-by: Roy Luo Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20230504000129.728316-1-royluo@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/ep0.c | 3 +++ drivers/usb/dwc3/gadget.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 953b752a5052..b94243237293 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -1207,5 +1207,8 @@ void dwc3_ep0_interrupt(struct dwc3 *dwc, dep->flags &= ~DWC3_EP_TRANSFER_STARTED; } break; + default: + dev_err(dwc->dev, "unknown endpoint event %d\n", event->endpoint_event); + break; } } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c0ca4d12f95d..7163d5d0eea0 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -3803,6 +3803,9 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, break; case DWC3_DEPEVT_RXTXFIFOEVT: break; + default: + dev_err(dwc->dev, "unknown endpoint event %d\n", event->endpoint_event); + break; } } -- cgit v1.2.3 From ada050c69108bc34be13ecc11f7fad0f20ebadc4 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 5 May 2023 19:15:08 +0200 Subject: usb: dwc2: Fix some error handling paths dwc2_driver_probe() calls dwc2_lowlevel_hw_init() which deassert some reset lines. Should an error happen in dwc2_lowlevel_hw_init() after calling reset_control_deassert() or in the probe after calling dwc2_lowlevel_hw_init(), the reset lines remain deasserted. Add some devm_add_action_or_reset() calls to re-assert the lines if needed. Update the remove function accordingly. This change is compile-tested only. Fixes: 83f8da562f8b ("usb: dwc2: Add reset control to dwc2") Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/c64537b5339342bd00f7c2152b8fc23792b9f95a.1683306479.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/platform.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 5aee284018c0..5cf025511cce 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -203,6 +203,11 @@ int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg) return ret; } +static void dwc2_reset_control_assert(void *data) +{ + reset_control_assert(data); +} + static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) { int i, ret; @@ -213,6 +218,10 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) "error getting reset control\n"); reset_control_deassert(hsotg->reset); + ret = devm_add_action_or_reset(hsotg->dev, dwc2_reset_control_assert, + hsotg->reset); + if (ret) + return ret; hsotg->reset_ecc = devm_reset_control_get_optional(hsotg->dev, "dwc2-ecc"); if (IS_ERR(hsotg->reset_ecc)) @@ -220,6 +229,10 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) "error getting reset control for ecc\n"); reset_control_deassert(hsotg->reset_ecc); + ret = devm_add_action_or_reset(hsotg->dev, dwc2_reset_control_assert, + hsotg->reset_ecc); + if (ret) + return ret; /* * Attempt to find a generic PHY, then look for an old style @@ -339,9 +352,6 @@ static int dwc2_driver_remove(struct platform_device *dev) if (hsotg->ll_hw_enabled) dwc2_lowlevel_hw_disable(hsotg); - reset_control_assert(hsotg->reset); - reset_control_assert(hsotg->reset_ecc); - return 0; } -- cgit v1.2.3 From 813f44d57e19ccaa7330e829bd913515be42719d Mon Sep 17 00:00:00 2001 From: Krishna Kurapati Date: Wed, 10 May 2023 13:22:51 +0530 Subject: usb: dwc3: gadget: Bail out in pullup if soft reset timeout happens If the core soft reset timeout happens, avoid setting up event buffers and starting gadget as the writes to these registers may not reflect when in reset and setting the run stop bit can lead the controller to access wrong event buffer address resulting in a crash. Signed-off-by: Krishna Kurapati Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20230510075252.31023-2-quic_kriskura@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 7163d5d0eea0..5965796bc5d5 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2746,13 +2746,16 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) * device-initiated disconnect requires a core soft reset * (DCTL.CSftRst) before enabling the run/stop bit. */ - dwc3_core_soft_reset(dwc); + ret = dwc3_core_soft_reset(dwc); + if (ret) + goto done; dwc3_event_buffers_setup(dwc); __dwc3_gadget_start(dwc); ret = dwc3_gadget_run_stop(dwc, true); } +done: pm_runtime_put(dwc->dev); return ret; -- cgit v1.2.3 From d34f9bafa78da2a561c67d9daf55fc4d1d80edf0 Mon Sep 17 00:00:00 2001 From: Krishna Kurapati Date: Wed, 10 May 2023 13:22:52 +0530 Subject: usb: gadget: udc: Handle gadget_connect failure during bind operation In the event, gadget_connect call (which invokes pullup) fails, propagate the error to udc bind operation which inturn sends the error to configfs. The userspace can then retry enumeartion if it chooses to. Signed-off-by: Krishna Kurapati Acked-by: Alan Stern Link: https://lore.kernel.org/r/20230510075252.31023-3-quic_kriskura@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/core.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 4641153e9706..69041cca5d24 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1122,12 +1122,16 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_state); /* ------------------------------------------------------------------------- */ /* Acquire connect_lock before calling this function. */ -static void usb_udc_connect_control_locked(struct usb_udc *udc) __must_hold(&udc->connect_lock) +static int usb_udc_connect_control_locked(struct usb_udc *udc) __must_hold(&udc->connect_lock) { + int ret; + if (udc->vbus && udc->started) - usb_gadget_connect_locked(udc->gadget); + ret = usb_gadget_connect_locked(udc->gadget); else - usb_gadget_disconnect_locked(udc->gadget); + ret = usb_gadget_disconnect_locked(udc->gadget); + + return ret; } /** @@ -1583,12 +1587,21 @@ static int gadget_bind_driver(struct device *dev) goto err_start; } usb_gadget_enable_async_callbacks(udc); - usb_udc_connect_control_locked(udc); + ret = usb_udc_connect_control_locked(udc); + if (ret) + goto err_connect_control; + mutex_unlock(&udc->connect_lock); kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); return 0; + err_connect_control: + usb_gadget_disable_async_callbacks(udc); + if (gadget->irq) + synchronize_irq(gadget->irq); + usb_gadget_udc_stop_locked(udc); + err_start: driver->unbind(udc->gadget); -- cgit v1.2.3 From be877fbf8968f870279c8384b41c6bd6c3863068 Mon Sep 17 00:00:00 2001 From: Dmitry Rokosov Date: Fri, 12 May 2023 00:04:53 +0300 Subject: usb: dwc2: support dwc2 IP for Amlogic A1 SoC family The Amlogic A1 uses dwc2 Synopsys IP as its USB peripheral (gadget) endpoint, with different DWC2 parameters when compared to previous Amlogic SoCs. Signed-off-by: Dmitry Rokosov Reviewed-by: Neil Armstrong Acked-by: Minas Harutyunyan Link: https://lore.kernel.org/r/20230511210455.6634-2-ddrokosov@sberdevices.ru Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/params.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c index 21d16533bd2f..4c7c3dd15f9b 100644 --- a/drivers/usb/dwc2/params.c +++ b/drivers/usb/dwc2/params.c @@ -161,6 +161,25 @@ static void dwc2_set_amlogic_g12a_params(struct dwc2_hsotg *hsotg) p->hird_threshold_en = false; } +static void dwc2_set_amlogic_a1_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->speed = DWC2_SPEED_PARAM_HIGH; + p->host_rx_fifo_size = 192; + p->host_nperio_tx_fifo_size = 128; + p->host_perio_tx_fifo_size = 128; + p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI; + p->phy_utmi_width = 8; + p->ahbcfg = GAHBCFG_HBSTLEN_INCR8 << GAHBCFG_HBSTLEN_SHIFT; + p->lpm = false; + p->lpm_clock_gating = false; + p->besl = false; + p->hird_threshold_en = false; +} + static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg) { struct dwc2_core_params *p = &hsotg->params; @@ -258,6 +277,8 @@ const struct of_device_id dwc2_of_match_table[] = { .data = dwc2_set_amlogic_params }, { .compatible = "amlogic,meson-g12a-usb", .data = dwc2_set_amlogic_g12a_params }, + { .compatible = "amlogic,meson-a1-usb", + .data = dwc2_set_amlogic_a1_params }, { .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params }, { .compatible = "apm,apm82181-dwc-otg", .data = dwc2_set_amcc_params }, { .compatible = "st,stm32f4x9-fsotg", -- cgit v1.2.3 From a9889e71b5e90665bfa90d8dd350caa2d3ee7797 Mon Sep 17 00:00:00 2001 From: Dmitry Rokosov Date: Fri, 12 May 2023 00:04:54 +0300 Subject: dt-bindings: usb: dwc2: add support for Amlogic A1 SoC USB peripheral Provide the appropriate compatible string for the DWC2 IP that is found inside the Amlogic A1 SoC and used in peripheral mode. Signed-off-by: Dmitry Rokosov Acked-by: Rob Herring Reviewed-by: Martin Blumenstingl Link: https://lore.kernel.org/r/20230511210455.6634-3-ddrokosov@sberdevices.ru Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/dwc2.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/usb/dwc2.yaml b/Documentation/devicetree/bindings/usb/dwc2.yaml index d3506090f8b1..0a5c98ea711d 100644 --- a/Documentation/devicetree/bindings/usb/dwc2.yaml +++ b/Documentation/devicetree/bindings/usb/dwc2.yaml @@ -53,6 +53,7 @@ properties: - amlogic,meson8b-usb - amlogic,meson-gxbb-usb - amlogic,meson-g12a-usb + - amlogic,meson-a1-usb - intel,socfpga-agilex-hsotg - const: snps,dwc2 - const: amcc,dwc-otg -- cgit v1.2.3 From 6bae03b0484b54f699d69339fbec5658e885c224 Mon Sep 17 00:00:00 2001 From: Dmitry Rokosov Date: Fri, 12 May 2023 00:04:55 +0300 Subject: usb: dwc3-meson-g12a: support OTG switch for all IP versions From now, the Amlogic A1 USB controller is capable of switching between host and gadget modes based on the status of the OTG_ID signal or via manual USB role change. Previously, only the Amlogic A1 IP version did not use OTG support for host only mode, but this is no longer applicable. Therefore, the 'otg_switch_supported' option can now be removed as it is no longer required. Signed-off-by: Dmitry Rokosov Reviewed-by: Neil Armstrong Reviewed-by: Martin Blumenstingl Link: https://lore.kernel.org/r/20230511210455.6634-4-ddrokosov@sberdevices.ru Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-meson-g12a.c | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index b282ad0e69c6..a13afdb219e8 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -140,7 +140,6 @@ static const char * const meson_a1_phy_names[] = { struct dwc3_meson_g12a; struct dwc3_meson_g12a_drvdata { - bool otg_switch_supported; bool otg_phy_host_port_disable; struct clk_bulk_data *clks; int num_clks; @@ -189,7 +188,6 @@ static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv); */ static const struct dwc3_meson_g12a_drvdata gxl_drvdata = { - .otg_switch_supported = true, .otg_phy_host_port_disable = true, .clks = meson_gxl_clocks, .num_clks = ARRAY_SIZE(meson_g12a_clocks), @@ -203,7 +201,6 @@ static const struct dwc3_meson_g12a_drvdata gxl_drvdata = { }; static const struct dwc3_meson_g12a_drvdata gxm_drvdata = { - .otg_switch_supported = true, .otg_phy_host_port_disable = true, .clks = meson_gxl_clocks, .num_clks = ARRAY_SIZE(meson_g12a_clocks), @@ -217,7 +214,6 @@ static const struct dwc3_meson_g12a_drvdata gxm_drvdata = { }; static const struct dwc3_meson_g12a_drvdata axg_drvdata = { - .otg_switch_supported = true, .clks = meson_gxl_clocks, .num_clks = ARRAY_SIZE(meson_gxl_clocks), .phy_names = meson_a1_phy_names, @@ -230,7 +226,6 @@ static const struct dwc3_meson_g12a_drvdata axg_drvdata = { }; static const struct dwc3_meson_g12a_drvdata g12a_drvdata = { - .otg_switch_supported = true, .clks = meson_g12a_clocks, .num_clks = ARRAY_SIZE(meson_g12a_clocks), .phy_names = meson_g12a_phy_names, @@ -242,7 +237,6 @@ static const struct dwc3_meson_g12a_drvdata g12a_drvdata = { }; static const struct dwc3_meson_g12a_drvdata a1_drvdata = { - .otg_switch_supported = false, .clks = meson_a1_clocks, .num_clks = ARRAY_SIZE(meson_a1_clocks), .phy_names = meson_a1_phy_names, @@ -307,7 +301,7 @@ static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i, U2P_R0_POWER_ON_RESET, U2P_R0_POWER_ON_RESET); - if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) { + if (i == USB2_OTG_PHY) { regmap_update_bits(priv->u2p_regmap[i], U2P_R0, U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS, U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS); @@ -490,7 +484,7 @@ static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv, { int ret; - if (!priv->drvdata->otg_switch_supported || !priv->phys[USB2_OTG_PHY]) + if (!priv->phys[USB2_OTG_PHY]) return -EINVAL; if (mode == PHY_MODE_USB_HOST) @@ -589,9 +583,6 @@ static int dwc3_meson_g12a_otg_init(struct platform_device *pdev, int ret, irq; struct device *dev = &pdev->dev; - if (!priv->drvdata->otg_switch_supported) - return 0; - if (priv->otg_mode == USB_DR_MODE_OTG) { /* Ack irq before registering */ regmap_update_bits(priv->usb_glue_regmap, USB_R5, @@ -841,8 +832,7 @@ static int dwc3_meson_g12a_remove(struct platform_device *pdev) struct device *dev = &pdev->dev; int i; - if (priv->drvdata->otg_switch_supported) - usb_role_switch_unregister(priv->role_switch); + usb_role_switch_unregister(priv->role_switch); of_platform_depopulate(dev); -- cgit v1.2.3 From 00bb478b829e68b856681002f52b00f8581316e6 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 8 May 2023 15:23:00 +0100 Subject: dt-bindings: usb: Add Qualcomm PMIC Type-C Add a description for the Type-C silicon interface inside Qualcomm's PM8150b hardware block. Based on original work by Wesley. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Wesley Cheng Signed-off-by: Bryan O'Donoghue Link: https://lore.kernel.org/r/20230508142308.1656410-6-bryan.odonoghue@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/qcom,pmic-typec.yaml | 190 +++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml diff --git a/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml b/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml new file mode 100644 index 000000000000..55df3129a0bc --- /dev/null +++ b/Documentation/devicetree/bindings/usb/qcom,pmic-typec.yaml @@ -0,0 +1,190 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/qcom,pmic-typec.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm PMIC based USB Type-C block + +maintainers: + - Bryan O'Donoghue + +description: + Qualcomm PMIC Type-C block + +properties: + compatible: + enum: + - qcom,pm8150b-typec + + connector: + type: object + $ref: /schemas/connector/usb-connector.yaml# + unevaluatedProperties: false + + reg: + description: Type-C port and pdphy SPMI register base offsets + maxItems: 2 + + interrupts: + items: + - description: Type-C CC attach notification, VBUS error, tCCDebounce done + - description: Type-C VCONN powered + - description: Type-C CC state change + - description: Type-C VCONN over-current + - description: Type-C VBUS state change + - description: Type-C Attach/detach notification + - description: Type-C Legacy cable detect + - description: Type-C Try.Src Try.Snk state change + - description: Power Domain Signal TX - HardReset or CableReset signal TX + - description: Power Domain Signal RX - HardReset or CableReset signal RX + - description: Power Domain TX complete + - description: Power Domain RX complete + - description: Power Domain TX fail + - description: Power Domain TX message discard + - description: Power Domain RX message discard + - description: Power Domain Fast Role Swap event + + interrupt-names: + items: + - const: or-rid-detect-change + - const: vpd-detect + - const: cc-state-change + - const: vconn-oc + - const: vbus-change + - const: attach-detach + - const: legacy-cable-detect + - const: try-snk-src-detect + - const: sig-tx + - const: sig-rx + - const: msg-tx + - const: msg-rx + - const: msg-tx-failed + - const: msg-tx-discarded + - const: msg-rx-discarded + - const: fr-swap + + vdd-vbus-supply: + description: VBUS power supply. + + vdd-pdphy-supply: + description: VDD regulator supply to the PDPHY. + + port: + $ref: /schemas/graph.yaml#/properties/port + description: + Contains a port which produces data-role switching messages. + +required: + - compatible + - reg + - interrupts + - interrupt-names + - vdd-vbus-supply + - vdd-pdphy-supply + +additionalProperties: false + +examples: + - | + #include + #include + + pmic { + #address-cells = <1>; + #size-cells = <0>; + + pm8150b_typec: typec@1500 { + compatible = "qcom,pm8150b-typec"; + reg = <0x1500>, + <0x1700>; + + interrupts = <0x2 0x15 0x00 IRQ_TYPE_EDGE_RISING>, + <0x2 0x15 0x01 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x15 0x02 IRQ_TYPE_EDGE_RISING>, + <0x2 0x15 0x03 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x15 0x04 IRQ_TYPE_EDGE_RISING>, + <0x2 0x15 0x05 IRQ_TYPE_EDGE_RISING>, + <0x2 0x15 0x06 IRQ_TYPE_EDGE_BOTH>, + <0x2 0x15 0x07 IRQ_TYPE_EDGE_RISING>, + <0x2 0x17 0x00 IRQ_TYPE_EDGE_RISING>, + <0x2 0x17 0x01 IRQ_TYPE_EDGE_RISING>, + <0x2 0x17 0x02 IRQ_TYPE_EDGE_RISING>, + <0x2 0x17 0x03 IRQ_TYPE_EDGE_RISING>, + <0x2 0x17 0x04 IRQ_TYPE_EDGE_RISING>, + <0x2 0x17 0x05 IRQ_TYPE_EDGE_RISING>, + <0x2 0x17 0x06 IRQ_TYPE_EDGE_RISING>, + <0x2 0x17 0x07 IRQ_TYPE_EDGE_RISING>; + + interrupt-names = "or-rid-detect-change", + "vpd-detect", + "cc-state-change", + "vconn-oc", + "vbus-change", + "attach-detach", + "legacy-cable-detect", + "try-snk-src-detect", + "sig-tx", + "sig-rx", + "msg-tx", + "msg-rx", + "msg-tx-failed", + "msg-tx-discarded", + "msg-rx-discarded", + "fr-swap"; + + vdd-vbus-supply = <&pm8150b_vbus>; + vdd-pdphy-supply = <&vreg_l2a_3p1>; + + connector { + compatible = "usb-c-connector"; + + power-role = "source"; + data-role = "dual"; + self-powered; + + source-pdos = ; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + pmic_typec_mux_out: endpoint { + remote-endpoint = <&usb_phy_typec_mux_in>; + }; + }; + + port@1 { + reg = <1>; + pmic_typec_role_switch_out: endpoint { + remote-endpoint = <&usb_role_switch_in>; + }; + }; + }; + }; + }; + }; + + usb { + dr_mode = "otg"; + usb-role-switch; + port { + usb_role_switch_in: endpoint { + remote-endpoint = <&pmic_typec_role_switch_out>; + }; + }; + }; + + usb-phy { + orientation-switch; + port { + usb_phy_typec_mux_in: endpoint { + remote-endpoint = <&pmic_typec_mux_out>; + }; + }; + }; + +... -- cgit v1.2.3 From a4422ff221429c600c3dc5d0394fb3738b89d040 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 8 May 2023 15:23:02 +0100 Subject: usb: typec: qcom: Add Qualcomm PMIC Type-C driver This commit adds a QCOM PMIC TCPM driver with an initial pm8150b block. The driver is layered as follows: qcom_pmic_typec.c : Responsible for registering with TCPM and arbitrates access to the Type-C and PDPHY hardware blocks in one place. This presents a single TCPM device to device to the Linux TCPM layer. qcom_pmic_typec_pdphy.c: Responsible for interfacing with the PDPHY hardware and processing power-delivery related calls from TCPM. This hardware binding can be extended to facilitate similar hardware in different PMICs. qcom_pmic_typec_port.c: Responsible for notifying and processing Type-C related calls from TCPM. Similar to the pdphy this layer can be extended to handle the specifics of different Qualcomm PMIC Type-C port managers. This code provides all of the same functionality as the existing qcom typec driver plus power-delivery as well. As a result commit 6c8cf3695176 ("usb: typec: Add QCOM PMIC typec detection driver") can be deleted entirely. References code from Jonathan Marek, Jack Pham, Wesley Cheng, Hemant Kumar, Guru Das Srinagesh and Ashay Jaiswal. Acked-by: Heikki Krogerus Reviewed-by: Guenter Roeck Reviewed-by: Caleb Connolly Signed-off-by: Bryan O'Donoghue Link: https://lore.kernel.org/r/20230508142308.1656410-8-bryan.odonoghue@linaro.org Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 10 + drivers/usb/typec/Kconfig | 13 - drivers/usb/typec/Makefile | 1 - drivers/usb/typec/qcom-pmic-typec.c | 261 ---------- drivers/usb/typec/tcpm/Kconfig | 11 + drivers/usb/typec/tcpm/Makefile | 1 + drivers/usb/typec/tcpm/qcom/Makefile | 6 + drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c | 346 +++++++++++++ .../usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c | 528 +++++++++++++++++++ .../usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.h | 119 +++++ drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c | 556 +++++++++++++++++++++ drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.h | 195 ++++++++ 12 files changed, 1772 insertions(+), 275 deletions(-) delete mode 100644 drivers/usb/typec/qcom-pmic-typec.c create mode 100644 drivers/usb/typec/tcpm/qcom/Makefile create mode 100644 drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c create mode 100644 drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c create mode 100644 drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.h create mode 100644 drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c create mode 100644 drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.h diff --git a/MAINTAINERS b/MAINTAINERS index 7e0b87d5aa2e..b5424199c32d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17533,6 +17533,16 @@ S: Maintained F: Documentation/devicetree/bindings/thermal/qcom-tsens.yaml F: drivers/thermal/qcom/ +QUALCOMM TYPEC PORT MANAGER DRIVER +M: Bryan O'Donoghue +L: linux-arm-msm@vger.kernel.org +L: linux-usb@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/usb/qcom,pmic-*.yaml +F: drivers/usb/typec/tcpm/qcom/ +F: include/dt-bindings/usb/typec/qcom,pmic-pdphy.h +F: include/dt-bindings/usb/typec/qcom,pmic-typec.h + QUALCOMM VENUS VIDEO ACCELERATOR DRIVER M: Stanimir Varbanov M: Vikash Garodia diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index 831e7049977d..2f80c2792dbd 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -100,19 +100,6 @@ config TYPEC_STUSB160X If you choose to build this driver as a dynamically linked module, the module will be called stusb160x.ko. -config TYPEC_QCOM_PMIC - tristate "Qualcomm PMIC USB Type-C driver" - depends on ARCH_QCOM || COMPILE_TEST - depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH - help - Driver for supporting role switch over the Qualcomm PMIC. This will - handle the USB Type-C role and orientation detection reported by the - QCOM PMIC if the PMIC has the capability to handle USB Type-C - detection. - - It will also enable the VBUS output to connected devices when a - DFP connection is made. - config TYPEC_WUSB3801 tristate "Willsemi WUSB3801 Type-C port controller driver" depends on I2C diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index 4a83dad51a6c..7a368fea61bc 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile @@ -8,7 +8,6 @@ obj-$(CONFIG_TYPEC_UCSI) += ucsi/ obj-$(CONFIG_TYPEC_TPS6598X) += tipd/ obj-$(CONFIG_TYPEC_ANX7411) += anx7411.o obj-$(CONFIG_TYPEC_HD3SS3220) += hd3ss3220.o -obj-$(CONFIG_TYPEC_QCOM_PMIC) += qcom-pmic-typec.o obj-$(CONFIG_TYPEC_STUSB160X) += stusb160x.o obj-$(CONFIG_TYPEC_RT1719) += rt1719.o obj-$(CONFIG_TYPEC_WUSB3801) += wusb3801.o diff --git a/drivers/usb/typec/qcom-pmic-typec.c b/drivers/usb/typec/qcom-pmic-typec.c deleted file mode 100644 index 432ea62f1bab..000000000000 --- a/drivers/usb/typec/qcom-pmic-typec.c +++ /dev/null @@ -1,261 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2020, The Linux Foundation. All rights reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define TYPEC_MISC_STATUS 0xb -#define CC_ATTACHED BIT(0) -#define CC_ORIENTATION BIT(1) -#define SNK_SRC_MODE BIT(6) -#define TYPEC_MODE_CFG 0x44 -#define TYPEC_DISABLE_CMD BIT(0) -#define EN_SNK_ONLY BIT(1) -#define EN_SRC_ONLY BIT(2) -#define TYPEC_VCONN_CONTROL 0x46 -#define VCONN_EN_SRC BIT(0) -#define VCONN_EN_VAL BIT(1) -#define TYPEC_EXIT_STATE_CFG 0x50 -#define SEL_SRC_UPPER_REF BIT(2) -#define TYPEC_INTR_EN_CFG_1 0x5e -#define TYPEC_INTR_EN_CFG_1_MASK GENMASK(7, 0) - -struct qcom_pmic_typec { - struct device *dev; - struct regmap *regmap; - u32 base; - - struct typec_port *port; - struct usb_role_switch *role_sw; - - struct regulator *vbus_reg; - bool vbus_enabled; -}; - -static void qcom_pmic_typec_enable_vbus_regulator(struct qcom_pmic_typec - *qcom_usb, bool enable) -{ - int ret; - - if (enable == qcom_usb->vbus_enabled) - return; - - if (enable) { - ret = regulator_enable(qcom_usb->vbus_reg); - if (ret) - return; - } else { - ret = regulator_disable(qcom_usb->vbus_reg); - if (ret) - return; - } - qcom_usb->vbus_enabled = enable; -} - -static void qcom_pmic_typec_check_connection(struct qcom_pmic_typec *qcom_usb) -{ - enum typec_orientation orientation; - enum usb_role role; - unsigned int stat; - bool enable_vbus; - - regmap_read(qcom_usb->regmap, qcom_usb->base + TYPEC_MISC_STATUS, - &stat); - - if (stat & CC_ATTACHED) { - orientation = (stat & CC_ORIENTATION) ? - TYPEC_ORIENTATION_REVERSE : - TYPEC_ORIENTATION_NORMAL; - typec_set_orientation(qcom_usb->port, orientation); - - role = (stat & SNK_SRC_MODE) ? USB_ROLE_HOST : USB_ROLE_DEVICE; - if (role == USB_ROLE_HOST) - enable_vbus = true; - else - enable_vbus = false; - } else { - role = USB_ROLE_NONE; - enable_vbus = false; - } - - qcom_pmic_typec_enable_vbus_regulator(qcom_usb, enable_vbus); - usb_role_switch_set_role(qcom_usb->role_sw, role); -} - -static irqreturn_t qcom_pmic_typec_interrupt(int irq, void *_qcom_usb) -{ - struct qcom_pmic_typec *qcom_usb = _qcom_usb; - - qcom_pmic_typec_check_connection(qcom_usb); - return IRQ_HANDLED; -} - -static void qcom_pmic_typec_typec_hw_init(struct qcom_pmic_typec *qcom_usb, - enum typec_port_type type) -{ - u8 mode = 0; - - regmap_update_bits(qcom_usb->regmap, - qcom_usb->base + TYPEC_INTR_EN_CFG_1, - TYPEC_INTR_EN_CFG_1_MASK, 0); - - if (type == TYPEC_PORT_SRC) - mode = EN_SRC_ONLY; - else if (type == TYPEC_PORT_SNK) - mode = EN_SNK_ONLY; - - regmap_update_bits(qcom_usb->regmap, qcom_usb->base + TYPEC_MODE_CFG, - EN_SNK_ONLY | EN_SRC_ONLY, mode); - - regmap_update_bits(qcom_usb->regmap, - qcom_usb->base + TYPEC_VCONN_CONTROL, - VCONN_EN_SRC | VCONN_EN_VAL, VCONN_EN_SRC); - regmap_update_bits(qcom_usb->regmap, - qcom_usb->base + TYPEC_EXIT_STATE_CFG, - SEL_SRC_UPPER_REF, SEL_SRC_UPPER_REF); -} - -static int qcom_pmic_typec_probe(struct platform_device *pdev) -{ - struct qcom_pmic_typec *qcom_usb; - struct device *dev = &pdev->dev; - struct fwnode_handle *fwnode; - struct typec_capability cap; - const char *buf; - int ret, irq, role; - u32 reg; - - ret = device_property_read_u32(dev, "reg", ®); - if (ret < 0) { - dev_err(dev, "missing base address\n"); - return ret; - } - - qcom_usb = devm_kzalloc(dev, sizeof(*qcom_usb), GFP_KERNEL); - if (!qcom_usb) - return -ENOMEM; - - qcom_usb->dev = dev; - qcom_usb->base = reg; - - qcom_usb->regmap = dev_get_regmap(dev->parent, NULL); - if (!qcom_usb->regmap) { - dev_err(dev, "Failed to get regmap\n"); - return -EINVAL; - } - - qcom_usb->vbus_reg = devm_regulator_get(qcom_usb->dev, "usb_vbus"); - if (IS_ERR(qcom_usb->vbus_reg)) - return PTR_ERR(qcom_usb->vbus_reg); - - fwnode = device_get_named_child_node(dev, "connector"); - if (!fwnode) - return -EINVAL; - - ret = fwnode_property_read_string(fwnode, "power-role", &buf); - if (!ret) { - role = typec_find_port_power_role(buf); - if (role < 0) - role = TYPEC_PORT_SNK; - } else { - role = TYPEC_PORT_SNK; - } - cap.type = role; - - ret = fwnode_property_read_string(fwnode, "data-role", &buf); - if (!ret) { - role = typec_find_port_data_role(buf); - if (role < 0) - role = TYPEC_PORT_UFP; - } else { - role = TYPEC_PORT_UFP; - } - cap.data = role; - - cap.prefer_role = TYPEC_NO_PREFERRED_ROLE; - cap.fwnode = fwnode; - qcom_usb->port = typec_register_port(dev, &cap); - if (IS_ERR(qcom_usb->port)) { - ret = PTR_ERR(qcom_usb->port); - dev_err(dev, "Failed to register type c port %d\n", ret); - goto err_put_node; - } - fwnode_handle_put(fwnode); - - qcom_usb->role_sw = fwnode_usb_role_switch_get(dev_fwnode(qcom_usb->dev)); - if (IS_ERR(qcom_usb->role_sw)) { - ret = dev_err_probe(dev, PTR_ERR(qcom_usb->role_sw), - "failed to get role switch\n"); - goto err_typec_port; - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - goto err_usb_role_sw; - - ret = devm_request_threaded_irq(qcom_usb->dev, irq, NULL, - qcom_pmic_typec_interrupt, IRQF_ONESHOT, - "qcom-pmic-typec", qcom_usb); - if (ret) { - dev_err(&pdev->dev, "Could not request IRQ\n"); - goto err_usb_role_sw; - } - - platform_set_drvdata(pdev, qcom_usb); - qcom_pmic_typec_typec_hw_init(qcom_usb, cap.type); - qcom_pmic_typec_check_connection(qcom_usb); - - return 0; - -err_usb_role_sw: - usb_role_switch_put(qcom_usb->role_sw); -err_typec_port: - typec_unregister_port(qcom_usb->port); -err_put_node: - fwnode_handle_put(fwnode); - - return ret; -} - -static int qcom_pmic_typec_remove(struct platform_device *pdev) -{ - struct qcom_pmic_typec *qcom_usb = platform_get_drvdata(pdev); - - usb_role_switch_set_role(qcom_usb->role_sw, USB_ROLE_NONE); - qcom_pmic_typec_enable_vbus_regulator(qcom_usb, 0); - - typec_unregister_port(qcom_usb->port); - usb_role_switch_put(qcom_usb->role_sw); - - return 0; -} - -static const struct of_device_id qcom_pmic_typec_table[] = { - { .compatible = "qcom,pm8150b-usb-typec" }, - { } -}; -MODULE_DEVICE_TABLE(of, qcom_pmic_typec_table); - -static struct platform_driver qcom_pmic_typec = { - .driver = { - .name = "qcom,pmic-typec", - .of_match_table = qcom_pmic_typec_table, - }, - .probe = qcom_pmic_typec_probe, - .remove = qcom_pmic_typec_remove, -}; -module_platform_driver(qcom_pmic_typec); - -MODULE_DESCRIPTION("QCOM PMIC USB type C driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig index e6b88ca4a4b9..5d393f520fc2 100644 --- a/drivers/usb/typec/tcpm/Kconfig +++ b/drivers/usb/typec/tcpm/Kconfig @@ -76,4 +76,15 @@ config TYPEC_WCOVE To compile this driver as module, choose M here: the module will be called typec_wcove.ko +config TYPEC_QCOM_PMIC + tristate "Qualcomm PMIC USB Type-C Port Controller Manager driver" + depends on ARCH_QCOM || COMPILE_TEST + help + A Type-C port and Power Delivery driver which aggregates two + discrete pieces of silicon in the PM8150b PMIC block: the + Type-C port controller and the Power Delivery PHY. + + This driver enables Type-C role switching, orientation, Alternate + mode and Power Delivery support both for VBUS and VCONN. + endif # TYPEC_TCPM diff --git a/drivers/usb/typec/tcpm/Makefile b/drivers/usb/typec/tcpm/Makefile index 08e57bb499cb..7a8cad0c0bdb 100644 --- a/drivers/usb/typec/tcpm/Makefile +++ b/drivers/usb/typec/tcpm/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_TYPEC_MT6360) += tcpci_mt6360.o obj-$(CONFIG_TYPEC_TCPCI_MT6370) += tcpci_mt6370.o obj-$(CONFIG_TYPEC_TCPCI_MAXIM) += tcpci_maxim.o tcpci_maxim-y += tcpci_maxim_core.o maxim_contaminant.o +obj-$(CONFIG_TYPEC_QCOM_PMIC) += qcom/ diff --git a/drivers/usb/typec/tcpm/qcom/Makefile b/drivers/usb/typec/tcpm/qcom/Makefile new file mode 100644 index 000000000000..dc1e8832e197 --- /dev/null +++ b/drivers/usb/typec/tcpm/qcom/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +obj-$(CONFIG_TYPEC_QCOM_PMIC) += qcom_pmic_tcpm.o +qcom_pmic_tcpm-y += qcom_pmic_typec.o \ + qcom_pmic_typec_port.o \ + qcom_pmic_typec_pdphy.o diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c new file mode 100644 index 000000000000..191458ce4a06 --- /dev/null +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023, Linaro Ltd. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qcom_pmic_typec_pdphy.h" +#include "qcom_pmic_typec_port.h" + +struct pmic_typec_resources { + struct pmic_typec_pdphy_resources *pdphy_res; + struct pmic_typec_port_resources *port_res; +}; + +struct pmic_typec { + struct device *dev; + struct tcpm_port *tcpm_port; + struct tcpc_dev tcpc; + struct pmic_typec_pdphy *pmic_typec_pdphy; + struct pmic_typec_port *pmic_typec_port; + bool vbus_enabled; + struct mutex lock; /* VBUS state serialization */ +}; + +#define tcpc_to_tcpm(_tcpc_) container_of(_tcpc_, struct pmic_typec, tcpc) + +static int qcom_pmic_typec_get_vbus(struct tcpc_dev *tcpc) +{ + struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc); + int ret; + + mutex_lock(&tcpm->lock); + ret = tcpm->vbus_enabled || qcom_pmic_typec_port_get_vbus(tcpm->pmic_typec_port); + mutex_unlock(&tcpm->lock); + + return ret; +} + +static int qcom_pmic_typec_set_vbus(struct tcpc_dev *tcpc, bool on, bool sink) +{ + struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc); + int ret = 0; + + mutex_lock(&tcpm->lock); + if (tcpm->vbus_enabled == on) + goto done; + + ret = qcom_pmic_typec_port_set_vbus(tcpm->pmic_typec_port, on); + if (ret) + goto done; + + tcpm->vbus_enabled = on; + tcpm_vbus_change(tcpm->tcpm_port); + +done: + dev_dbg(tcpm->dev, "set_vbus set: %d result %d\n", on, ret); + mutex_unlock(&tcpm->lock); + + return ret; +} + +static int qcom_pmic_typec_set_vconn(struct tcpc_dev *tcpc, bool on) +{ + struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc); + + return qcom_pmic_typec_port_set_vconn(tcpm->pmic_typec_port, on); +} + +static int qcom_pmic_typec_get_cc(struct tcpc_dev *tcpc, + enum typec_cc_status *cc1, + enum typec_cc_status *cc2) +{ + struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc); + + return qcom_pmic_typec_port_get_cc(tcpm->pmic_typec_port, cc1, cc2); +} + +static int qcom_pmic_typec_set_cc(struct tcpc_dev *tcpc, + enum typec_cc_status cc) +{ + struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc); + + return qcom_pmic_typec_port_set_cc(tcpm->pmic_typec_port, cc); +} + +static int qcom_pmic_typec_set_polarity(struct tcpc_dev *tcpc, + enum typec_cc_polarity pol) +{ + /* Polarity is set separately by phy-qcom-qmp.c */ + return 0; +} + +static int qcom_pmic_typec_start_toggling(struct tcpc_dev *tcpc, + enum typec_port_type port_type, + enum typec_cc_status cc) +{ + struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc); + + return qcom_pmic_typec_port_start_toggling(tcpm->pmic_typec_port, + port_type, cc); +} + +static int qcom_pmic_typec_set_roles(struct tcpc_dev *tcpc, bool attached, + enum typec_role power_role, + enum typec_data_role data_role) +{ + struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc); + + return qcom_pmic_typec_pdphy_set_roles(tcpm->pmic_typec_pdphy, + data_role, power_role); +} + +static int qcom_pmic_typec_set_pd_rx(struct tcpc_dev *tcpc, bool on) +{ + struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc); + + return qcom_pmic_typec_pdphy_set_pd_rx(tcpm->pmic_typec_pdphy, on); +} + +static int qcom_pmic_typec_pd_transmit(struct tcpc_dev *tcpc, + enum tcpm_transmit_type type, + const struct pd_message *msg, + unsigned int negotiated_rev) +{ + struct pmic_typec *tcpm = tcpc_to_tcpm(tcpc); + + return qcom_pmic_typec_pdphy_pd_transmit(tcpm->pmic_typec_pdphy, type, + msg, negotiated_rev); +} + +static int qcom_pmic_typec_init(struct tcpc_dev *tcpc) +{ + return 0; +} + +static int qcom_pmic_typec_probe(struct platform_device *pdev) +{ + struct pmic_typec *tcpm; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + const struct pmic_typec_resources *res; + struct regmap *regmap; + u32 base[2]; + int ret; + + res = of_device_get_match_data(dev); + if (!res) + return -ENODEV; + + tcpm = devm_kzalloc(dev, sizeof(*tcpm), GFP_KERNEL); + if (!tcpm) + return -ENOMEM; + + tcpm->dev = dev; + tcpm->tcpc.init = qcom_pmic_typec_init; + tcpm->tcpc.get_vbus = qcom_pmic_typec_get_vbus; + tcpm->tcpc.set_vbus = qcom_pmic_typec_set_vbus; + tcpm->tcpc.set_cc = qcom_pmic_typec_set_cc; + tcpm->tcpc.get_cc = qcom_pmic_typec_get_cc; + tcpm->tcpc.set_polarity = qcom_pmic_typec_set_polarity; + tcpm->tcpc.set_vconn = qcom_pmic_typec_set_vconn; + tcpm->tcpc.start_toggling = qcom_pmic_typec_start_toggling; + tcpm->tcpc.set_pd_rx = qcom_pmic_typec_set_pd_rx; + tcpm->tcpc.set_roles = qcom_pmic_typec_set_roles; + tcpm->tcpc.pd_transmit = qcom_pmic_typec_pd_transmit; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) { + dev_err(dev, "Failed to get regmap\n"); + return -ENODEV; + } + + ret = of_property_read_u32_array(np, "reg", base, 2); + if (ret) + return ret; + + tcpm->pmic_typec_port = qcom_pmic_typec_port_alloc(dev); + if (IS_ERR(tcpm->pmic_typec_port)) + return PTR_ERR(tcpm->pmic_typec_port); + + tcpm->pmic_typec_pdphy = qcom_pmic_typec_pdphy_alloc(dev); + if (IS_ERR(tcpm->pmic_typec_pdphy)) + return PTR_ERR(tcpm->pmic_typec_pdphy); + + ret = qcom_pmic_typec_port_probe(pdev, tcpm->pmic_typec_port, + res->port_res, regmap, base[0]); + if (ret) + return ret; + + ret = qcom_pmic_typec_pdphy_probe(pdev, tcpm->pmic_typec_pdphy, + res->pdphy_res, regmap, base[1]); + if (ret) + return ret; + + mutex_init(&tcpm->lock); + platform_set_drvdata(pdev, tcpm); + + tcpm->tcpc.fwnode = device_get_named_child_node(tcpm->dev, "connector"); + if (IS_ERR(tcpm->tcpc.fwnode)) + return PTR_ERR(tcpm->tcpc.fwnode); + + tcpm->tcpm_port = tcpm_register_port(tcpm->dev, &tcpm->tcpc); + if (IS_ERR(tcpm->tcpm_port)) { + ret = PTR_ERR(tcpm->tcpm_port); + goto fwnode_remove; + } + + ret = qcom_pmic_typec_port_start(tcpm->pmic_typec_port, + tcpm->tcpm_port); + if (ret) + goto fwnode_remove; + + ret = qcom_pmic_typec_pdphy_start(tcpm->pmic_typec_pdphy, + tcpm->tcpm_port); + if (ret) + goto fwnode_remove; + + return 0; + +fwnode_remove: + fwnode_remove_software_node(tcpm->tcpc.fwnode); + + return ret; +} + +static int qcom_pmic_typec_remove(struct platform_device *pdev) +{ + struct pmic_typec *tcpm = platform_get_drvdata(pdev); + + qcom_pmic_typec_pdphy_stop(tcpm->pmic_typec_pdphy); + qcom_pmic_typec_port_stop(tcpm->pmic_typec_port); + tcpm_unregister_port(tcpm->tcpm_port); + fwnode_remove_software_node(tcpm->tcpc.fwnode); + + return 0; +} + +static struct pmic_typec_pdphy_resources pm8150b_pdphy_res = { + .irq_params = { + { + .virq = PMIC_PDPHY_SIG_TX_IRQ, + .irq_name = "sig-tx", + }, + { + .virq = PMIC_PDPHY_SIG_RX_IRQ, + .irq_name = "sig-rx", + }, + { + .virq = PMIC_PDPHY_MSG_TX_IRQ, + .irq_name = "msg-tx", + }, + { + .virq = PMIC_PDPHY_MSG_RX_IRQ, + .irq_name = "msg-rx", + }, + { + .virq = PMIC_PDPHY_MSG_TX_FAIL_IRQ, + .irq_name = "msg-tx-failed", + }, + { + .virq = PMIC_PDPHY_MSG_TX_DISCARD_IRQ, + .irq_name = "msg-tx-discarded", + }, + { + .virq = PMIC_PDPHY_MSG_RX_DISCARD_IRQ, + .irq_name = "msg-rx-discarded", + }, + }, + .nr_irqs = 7, +}; + +static struct pmic_typec_port_resources pm8150b_port_res = { + .irq_params = { + { + .irq_name = "vpd-detect", + .virq = PMIC_TYPEC_VPD_IRQ, + }, + + { + .irq_name = "cc-state-change", + .virq = PMIC_TYPEC_CC_STATE_IRQ, + }, + { + .irq_name = "vconn-oc", + .virq = PMIC_TYPEC_VCONN_OC_IRQ, + }, + + { + .irq_name = "vbus-change", + .virq = PMIC_TYPEC_VBUS_IRQ, + }, + + { + .irq_name = "attach-detach", + .virq = PMIC_TYPEC_ATTACH_DETACH_IRQ, + }, + { + .irq_name = "legacy-cable-detect", + .virq = PMIC_TYPEC_LEGACY_CABLE_IRQ, + }, + + { + .irq_name = "try-snk-src-detect", + .virq = PMIC_TYPEC_TRY_SNK_SRC_IRQ, + }, + }, + .nr_irqs = 7, +}; + +struct pmic_typec_resources pm8150b_typec_res = { + .pdphy_res = &pm8150b_pdphy_res, + .port_res = &pm8150b_port_res, +}; + +static const struct of_device_id qcom_pmic_typec_table[] = { + { .compatible = "qcom,pm8150b-typec", .data = &pm8150b_typec_res }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_pmic_typec_table); + +static struct platform_driver qcom_pmic_typec_driver = { + .driver = { + .name = "qcom,pmic-typec", + .of_match_table = qcom_pmic_typec_table, + }, + .probe = qcom_pmic_typec_probe, + .remove = qcom_pmic_typec_remove, +}; + +module_platform_driver(qcom_pmic_typec_driver); + +MODULE_DESCRIPTION("QCOM PMIC USB Type-C Port Manager Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c new file mode 100644 index 000000000000..4e1b846627d2 --- /dev/null +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c @@ -0,0 +1,528 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023, Linaro Ltd. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qcom_pmic_typec_pdphy.h" + +struct pmic_typec_pdphy_irq_data { + int virq; + int irq; + struct pmic_typec_pdphy *pmic_typec_pdphy; +}; + +struct pmic_typec_pdphy { + struct device *dev; + struct tcpm_port *tcpm_port; + struct regmap *regmap; + u32 base; + + unsigned int nr_irqs; + struct pmic_typec_pdphy_irq_data *irq_data; + + struct work_struct reset_work; + struct work_struct receive_work; + struct regulator *vdd_pdphy; + spinlock_t lock; /* Register atomicity */ +}; + +static void qcom_pmic_typec_pdphy_reset_on(struct pmic_typec_pdphy *pmic_typec_pdphy) +{ + struct device *dev = pmic_typec_pdphy->dev; + int ret; + + /* Terminate TX */ + ret = regmap_write(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_TX_CONTROL_REG, 0); + if (ret) + goto err; + + ret = regmap_write(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_FRAME_FILTER_REG, 0); + if (ret) + goto err; + + return; +err: + dev_err(dev, "pd_reset_on error\n"); +} + +static void qcom_pmic_typec_pdphy_reset_off(struct pmic_typec_pdphy *pmic_typec_pdphy) +{ + struct device *dev = pmic_typec_pdphy->dev; + int ret; + + ret = regmap_write(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_FRAME_FILTER_REG, + FRAME_FILTER_EN_SOP | FRAME_FILTER_EN_HARD_RESET); + if (ret) + dev_err(dev, "pd_reset_off error\n"); +} + +static void qcom_pmic_typec_pdphy_sig_reset_work(struct work_struct *work) +{ + struct pmic_typec_pdphy *pmic_typec_pdphy = container_of(work, struct pmic_typec_pdphy, + reset_work); + unsigned long flags; + + spin_lock_irqsave(&pmic_typec_pdphy->lock, flags); + + qcom_pmic_typec_pdphy_reset_on(pmic_typec_pdphy); + qcom_pmic_typec_pdphy_reset_off(pmic_typec_pdphy); + + spin_unlock_irqrestore(&pmic_typec_pdphy->lock, flags); + + tcpm_pd_hard_reset(pmic_typec_pdphy->tcpm_port); +} + +static int +qcom_pmic_typec_pdphy_clear_tx_control_reg(struct pmic_typec_pdphy *pmic_typec_pdphy) +{ + struct device *dev = pmic_typec_pdphy->dev; + unsigned int val; + int ret; + + /* Clear TX control register */ + ret = regmap_write(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_TX_CONTROL_REG, 0); + if (ret) + goto done; + + /* Perform readback to ensure sufficient delay for command to latch */ + ret = regmap_read(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_TX_CONTROL_REG, &val); + +done: + if (ret) + dev_err(dev, "pd_clear_tx_control_reg: clear tx flag\n"); + + return ret; +} + +static int +qcom_pmic_typec_pdphy_pd_transmit_signal(struct pmic_typec_pdphy *pmic_typec_pdphy, + enum tcpm_transmit_type type, + unsigned int negotiated_rev) +{ + struct device *dev = pmic_typec_pdphy->dev; + unsigned int val; + unsigned long flags; + int ret; + + spin_lock_irqsave(&pmic_typec_pdphy->lock, flags); + + /* Clear TX control register */ + ret = qcom_pmic_typec_pdphy_clear_tx_control_reg(pmic_typec_pdphy); + if (ret) + goto done; + + val = TX_CONTROL_SEND_SIGNAL; + if (negotiated_rev == PD_REV30) + val |= TX_CONTROL_RETRY_COUNT(2); + else + val |= TX_CONTROL_RETRY_COUNT(3); + + if (type == TCPC_TX_CABLE_RESET || type == TCPC_TX_HARD_RESET) + val |= TX_CONTROL_FRAME_TYPE(1); + + ret = regmap_write(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_TX_CONTROL_REG, val); + +done: + spin_unlock_irqrestore(&pmic_typec_pdphy->lock, flags); + + dev_vdbg(dev, "pd_transmit_signal: type %d negotiate_rev %d send %d\n", + type, negotiated_rev, ret); + + return ret; +} + +static int +qcom_pmic_typec_pdphy_pd_transmit_payload(struct pmic_typec_pdphy *pmic_typec_pdphy, + enum tcpm_transmit_type type, + const struct pd_message *msg, + unsigned int negotiated_rev) +{ + struct device *dev = pmic_typec_pdphy->dev; + unsigned int val, hdr_len, txbuf_len, txsize_len; + unsigned long flags; + int ret; + + spin_lock_irqsave(&pmic_typec_pdphy->lock, flags); + + ret = regmap_read(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_RX_ACKNOWLEDGE_REG, + &val); + if (ret) + goto done; + + if (val) { + dev_err(dev, "pd_transmit_payload: RX message pending\n"); + ret = -EBUSY; + goto done; + } + + /* Clear TX control register */ + ret = qcom_pmic_typec_pdphy_clear_tx_control_reg(pmic_typec_pdphy); + if (ret) + goto done; + + hdr_len = sizeof(msg->header); + txbuf_len = pd_header_cnt_le(msg->header) * 4; + txsize_len = hdr_len + txbuf_len - 1; + + /* Write message header sizeof(u16) to USB_PDPHY_TX_BUFFER_HDR_REG */ + ret = regmap_bulk_write(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_TX_BUFFER_HDR_REG, + &msg->header, hdr_len); + if (ret) + goto done; + + /* Write payload to USB_PDPHY_TX_BUFFER_DATA_REG for txbuf_len */ + if (txbuf_len) { + ret = regmap_bulk_write(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_TX_BUFFER_DATA_REG, + &msg->payload, txbuf_len); + if (ret) + goto done; + } + + /* Write total length ((header + data) - 1) to USB_PDPHY_TX_SIZE_REG */ + ret = regmap_write(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_TX_SIZE_REG, + txsize_len); + if (ret) + goto done; + + /* Clear TX control register */ + ret = qcom_pmic_typec_pdphy_clear_tx_control_reg(pmic_typec_pdphy); + if (ret) + goto done; + + /* Initiate transmit with retry count as indicated by PD revision */ + val = TX_CONTROL_FRAME_TYPE(type) | TX_CONTROL_SEND_MSG; + if (pd_header_rev(msg->header) == PD_REV30) + val |= TX_CONTROL_RETRY_COUNT(2); + else + val |= TX_CONTROL_RETRY_COUNT(3); + + ret = regmap_write(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_TX_CONTROL_REG, val); + +done: + spin_unlock_irqrestore(&pmic_typec_pdphy->lock, flags); + + if (ret) { + dev_err(dev, "pd_transmit_payload: hdr %*ph data %*ph ret %d\n", + hdr_len, &msg->header, txbuf_len, &msg->payload, ret); + } + + return ret; +} + +int qcom_pmic_typec_pdphy_pd_transmit(struct pmic_typec_pdphy *pmic_typec_pdphy, + enum tcpm_transmit_type type, + const struct pd_message *msg, + unsigned int negotiated_rev) +{ + struct device *dev = pmic_typec_pdphy->dev; + int ret; + + if (msg) { + ret = qcom_pmic_typec_pdphy_pd_transmit_payload(pmic_typec_pdphy, + type, msg, + negotiated_rev); + } else { + ret = qcom_pmic_typec_pdphy_pd_transmit_signal(pmic_typec_pdphy, + type, + negotiated_rev); + } + + if (ret) + dev_dbg(dev, "pd_transmit: type %x result %d\n", type, ret); + + return ret; +} + +static void qcom_pmic_typec_pdphy_pd_receive(struct pmic_typec_pdphy *pmic_typec_pdphy) +{ + struct device *dev = pmic_typec_pdphy->dev; + struct pd_message msg; + unsigned int size, rx_status; + unsigned long flags; + int ret; + + spin_lock_irqsave(&pmic_typec_pdphy->lock, flags); + + ret = regmap_read(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_RX_SIZE_REG, &size); + if (ret) + goto done; + + /* Hardware requires +1 of the real read value to be passed */ + if (size < 1 || size > sizeof(msg.payload) + 1) { + dev_dbg(dev, "pd_receive: invalid size %d\n", size); + goto done; + } + + size += 1; + ret = regmap_read(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_RX_STATUS_REG, + &rx_status); + + if (ret) + goto done; + + ret = regmap_bulk_read(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_RX_BUFFER_REG, + (u8 *)&msg, size); + if (ret) + goto done; + + /* Return ownership of RX buffer to hardware */ + ret = regmap_write(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_RX_ACKNOWLEDGE_REG, 0); + +done: + spin_unlock_irqrestore(&pmic_typec_pdphy->lock, flags); + + if (!ret) { + dev_vdbg(dev, "pd_receive: handing %d bytes to tcpm\n", size); + tcpm_pd_receive(pmic_typec_pdphy->tcpm_port, &msg); + } +} + +static irqreturn_t qcom_pmic_typec_pdphy_isr(int irq, void *dev_id) +{ + struct pmic_typec_pdphy_irq_data *irq_data = dev_id; + struct pmic_typec_pdphy *pmic_typec_pdphy = irq_data->pmic_typec_pdphy; + struct device *dev = pmic_typec_pdphy->dev; + + switch (irq_data->virq) { + case PMIC_PDPHY_SIG_TX_IRQ: + dev_err(dev, "isr: tx_sig\n"); + break; + case PMIC_PDPHY_SIG_RX_IRQ: + schedule_work(&pmic_typec_pdphy->reset_work); + break; + case PMIC_PDPHY_MSG_TX_IRQ: + tcpm_pd_transmit_complete(pmic_typec_pdphy->tcpm_port, + TCPC_TX_SUCCESS); + break; + case PMIC_PDPHY_MSG_RX_IRQ: + qcom_pmic_typec_pdphy_pd_receive(pmic_typec_pdphy); + break; + case PMIC_PDPHY_MSG_TX_FAIL_IRQ: + tcpm_pd_transmit_complete(pmic_typec_pdphy->tcpm_port, + TCPC_TX_FAILED); + break; + case PMIC_PDPHY_MSG_TX_DISCARD_IRQ: + tcpm_pd_transmit_complete(pmic_typec_pdphy->tcpm_port, + TCPC_TX_DISCARDED); + break; + } + + return IRQ_HANDLED; +} + +int qcom_pmic_typec_pdphy_set_pd_rx(struct pmic_typec_pdphy *pmic_typec_pdphy, bool on) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&pmic_typec_pdphy->lock, flags); + + ret = regmap_write(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_RX_ACKNOWLEDGE_REG, !on); + + spin_unlock_irqrestore(&pmic_typec_pdphy->lock, flags); + + dev_dbg(pmic_typec_pdphy->dev, "set_pd_rx: %s\n", on ? "on" : "off"); + + return ret; +} + +int qcom_pmic_typec_pdphy_set_roles(struct pmic_typec_pdphy *pmic_typec_pdphy, + bool data_role_host, bool power_role_src) +{ + struct device *dev = pmic_typec_pdphy->dev; + unsigned long flags; + int ret; + + spin_lock_irqsave(&pmic_typec_pdphy->lock, flags); + + ret = regmap_update_bits(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_MSG_CONFIG_REG, + MSG_CONFIG_PORT_DATA_ROLE | + MSG_CONFIG_PORT_POWER_ROLE, + data_role_host << 3 | power_role_src << 2); + + spin_unlock_irqrestore(&pmic_typec_pdphy->lock, flags); + + dev_dbg(dev, "pdphy_set_roles: data_role_host=%d power_role_src=%d\n", + data_role_host, power_role_src); + + return ret; +} + +static int qcom_pmic_typec_pdphy_enable(struct pmic_typec_pdphy *pmic_typec_pdphy) +{ + struct device *dev = pmic_typec_pdphy->dev; + int ret; + + ret = regulator_enable(pmic_typec_pdphy->vdd_pdphy); + if (ret) + return ret; + + /* PD 2.0, DR=TYPEC_DEVICE, PR=TYPEC_SINK */ + ret = regmap_update_bits(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_MSG_CONFIG_REG, + MSG_CONFIG_SPEC_REV_MASK, PD_REV20); + if (ret) + goto done; + + ret = regmap_write(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_EN_CONTROL_REG, 0); + if (ret) + goto done; + + ret = regmap_write(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_EN_CONTROL_REG, + CONTROL_ENABLE); + if (ret) + goto done; + + qcom_pmic_typec_pdphy_reset_off(pmic_typec_pdphy); +done: + if (ret) { + regulator_disable(pmic_typec_pdphy->vdd_pdphy); + dev_err(dev, "pdphy_enable fail %d\n", ret); + } + + return ret; +} + +static int qcom_pmic_typec_pdphy_disable(struct pmic_typec_pdphy *pmic_typec_pdphy) +{ + int ret; + + qcom_pmic_typec_pdphy_reset_on(pmic_typec_pdphy); + + ret = regmap_write(pmic_typec_pdphy->regmap, + pmic_typec_pdphy->base + USB_PDPHY_EN_CONTROL_REG, 0); + + regulator_disable(pmic_typec_pdphy->vdd_pdphy); + + return ret; +} + +static int pmic_typec_pdphy_reset(struct pmic_typec_pdphy *pmic_typec_pdphy) +{ + int ret; + + ret = qcom_pmic_typec_pdphy_disable(pmic_typec_pdphy); + if (ret) + goto done; + + usleep_range(400, 500); + ret = qcom_pmic_typec_pdphy_enable(pmic_typec_pdphy); +done: + return ret; +} + +int qcom_pmic_typec_pdphy_start(struct pmic_typec_pdphy *pmic_typec_pdphy, + struct tcpm_port *tcpm_port) +{ + int i; + int ret; + + pmic_typec_pdphy->tcpm_port = tcpm_port; + + ret = pmic_typec_pdphy_reset(pmic_typec_pdphy); + if (ret) + return ret; + + for (i = 0; i < pmic_typec_pdphy->nr_irqs; i++) + enable_irq(pmic_typec_pdphy->irq_data[i].irq); + + return 0; +} + +void qcom_pmic_typec_pdphy_stop(struct pmic_typec_pdphy *pmic_typec_pdphy) +{ + int i; + + for (i = 0; i < pmic_typec_pdphy->nr_irqs; i++) + disable_irq(pmic_typec_pdphy->irq_data[i].irq); + + qcom_pmic_typec_pdphy_reset_on(pmic_typec_pdphy); +} + +struct pmic_typec_pdphy *qcom_pmic_typec_pdphy_alloc(struct device *dev) +{ + return devm_kzalloc(dev, sizeof(struct pmic_typec_pdphy), GFP_KERNEL); +} + +int qcom_pmic_typec_pdphy_probe(struct platform_device *pdev, + struct pmic_typec_pdphy *pmic_typec_pdphy, + struct pmic_typec_pdphy_resources *res, + struct regmap *regmap, + u32 base) +{ + struct device *dev = &pdev->dev; + struct pmic_typec_pdphy_irq_data *irq_data; + int i, ret, irq; + + if (!res->nr_irqs || res->nr_irqs > PMIC_PDPHY_MAX_IRQS) + return -EINVAL; + + irq_data = devm_kzalloc(dev, sizeof(*irq_data) * res->nr_irqs, + GFP_KERNEL); + if (!irq_data) + return -ENOMEM; + + pmic_typec_pdphy->vdd_pdphy = devm_regulator_get(dev, "vdd-pdphy"); + if (IS_ERR(pmic_typec_pdphy->vdd_pdphy)) + return PTR_ERR(pmic_typec_pdphy->vdd_pdphy); + + pmic_typec_pdphy->dev = dev; + pmic_typec_pdphy->base = base; + pmic_typec_pdphy->regmap = regmap; + pmic_typec_pdphy->nr_irqs = res->nr_irqs; + pmic_typec_pdphy->irq_data = irq_data; + spin_lock_init(&pmic_typec_pdphy->lock); + INIT_WORK(&pmic_typec_pdphy->reset_work, qcom_pmic_typec_pdphy_sig_reset_work); + + for (i = 0; i < res->nr_irqs; i++, irq_data++) { + irq = platform_get_irq_byname(pdev, res->irq_params[i].irq_name); + if (irq < 0) + return irq; + + irq_data->pmic_typec_pdphy = pmic_typec_pdphy; + irq_data->irq = irq; + irq_data->virq = res->irq_params[i].virq; + + ret = devm_request_threaded_irq(dev, irq, NULL, + qcom_pmic_typec_pdphy_isr, + IRQF_ONESHOT | IRQF_NO_AUTOEN, + res->irq_params[i].irq_name, + irq_data); + if (ret) + return ret; + } + + return 0; +} diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.h b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.h new file mode 100644 index 000000000000..e67954e31b14 --- /dev/null +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.h @@ -0,0 +1,119 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2023, Linaro Ltd. All rights reserved. + */ +#ifndef __QCOM_PMIC_PDPHY_H__ +#define __QCOM_PMIC_PDPHY_H__ + +#include +#include +#include + +#define USB_PDPHY_MAX_DATA_OBJ_LEN 28 +#define USB_PDPHY_MSG_HDR_LEN 2 + +/* PD PHY register offsets and bit fields */ +#define USB_PDPHY_MSG_CONFIG_REG 0x40 +#define MSG_CONFIG_PORT_DATA_ROLE BIT(3) +#define MSG_CONFIG_PORT_POWER_ROLE BIT(2) +#define MSG_CONFIG_SPEC_REV_MASK (BIT(1) | BIT(0)) + +#define USB_PDPHY_EN_CONTROL_REG 0x46 +#define CONTROL_ENABLE BIT(0) + +#define USB_PDPHY_RX_STATUS_REG 0x4A +#define RX_FRAME_TYPE (BIT(0) | BIT(1) | BIT(2)) + +#define USB_PDPHY_FRAME_FILTER_REG 0x4C +#define FRAME_FILTER_EN_HARD_RESET BIT(5) +#define FRAME_FILTER_EN_SOP BIT(0) + +#define USB_PDPHY_TX_SIZE_REG 0x42 +#define TX_SIZE_MASK 0xF + +#define USB_PDPHY_TX_CONTROL_REG 0x44 +#define TX_CONTROL_RETRY_COUNT(n) (((n) & 0x3) << 5) +#define TX_CONTROL_FRAME_TYPE(n) (((n) & 0x7) << 2) +#define TX_CONTROL_FRAME_TYPE_CABLE_RESET (0x1 << 2) +#define TX_CONTROL_SEND_SIGNAL BIT(1) +#define TX_CONTROL_SEND_MSG BIT(0) + +#define USB_PDPHY_RX_SIZE_REG 0x48 + +#define USB_PDPHY_RX_ACKNOWLEDGE_REG 0x4B +#define RX_BUFFER_TOKEN BIT(0) + +#define USB_PDPHY_BIST_MODE_REG 0x4E +#define BIST_MODE_MASK 0xF +#define BIST_ENABLE BIT(7) +#define PD_MSG_BIST 0x3 +#define PD_BIST_TEST_DATA_MODE 0x8 + +#define USB_PDPHY_TX_BUFFER_HDR_REG 0x60 +#define USB_PDPHY_TX_BUFFER_DATA_REG 0x62 + +#define USB_PDPHY_RX_BUFFER_REG 0x80 + +/* VDD regulator */ +#define VDD_PDPHY_VOL_MIN 2800000 /* uV */ +#define VDD_PDPHY_VOL_MAX 3300000 /* uV */ +#define VDD_PDPHY_HPM_LOAD 3000 /* uA */ + +/* Message Spec Rev field */ +#define PD_MSG_HDR_REV(hdr) (((hdr) >> 6) & 3) + +/* timers */ +#define RECEIVER_RESPONSE_TIME 15 /* tReceiverResponse */ +#define HARD_RESET_COMPLETE_TIME 5 /* tHardResetComplete */ + +/* Interrupt numbers */ +#define PMIC_PDPHY_SIG_TX_IRQ 0x0 +#define PMIC_PDPHY_SIG_RX_IRQ 0x1 +#define PMIC_PDPHY_MSG_TX_IRQ 0x2 +#define PMIC_PDPHY_MSG_RX_IRQ 0x3 +#define PMIC_PDPHY_MSG_TX_FAIL_IRQ 0x4 +#define PMIC_PDPHY_MSG_TX_DISCARD_IRQ 0x5 +#define PMIC_PDPHY_MSG_RX_DISCARD_IRQ 0x6 +#define PMIC_PDPHY_FR_SWAP_IRQ 0x7 + +/* Resources */ +#define PMIC_PDPHY_MAX_IRQS 0x08 + +struct pmic_typec_pdphy_irq_params { + int virq; + char *irq_name; +}; + +struct pmic_typec_pdphy_resources { + unsigned int nr_irqs; + struct pmic_typec_pdphy_irq_params irq_params[PMIC_PDPHY_MAX_IRQS]; +}; + +/* API */ +struct pmic_typec_pdphy; + +struct pmic_typec_pdphy *qcom_pmic_typec_pdphy_alloc(struct device *dev); + +int qcom_pmic_typec_pdphy_probe(struct platform_device *pdev, + struct pmic_typec_pdphy *pmic_typec_pdphy, + struct pmic_typec_pdphy_resources *res, + struct regmap *regmap, + u32 base); + +int qcom_pmic_typec_pdphy_start(struct pmic_typec_pdphy *pmic_typec_pdphy, + struct tcpm_port *tcpm_port); + +void qcom_pmic_typec_pdphy_stop(struct pmic_typec_pdphy *pmic_typec_pdphy); + +int qcom_pmic_typec_pdphy_set_roles(struct pmic_typec_pdphy *pmic_typec_pdphy, + bool power_role_src, bool data_role_host); + +int qcom_pmic_typec_pdphy_set_pd_rx(struct pmic_typec_pdphy *pmic_typec_pdphy, bool on); + +int qcom_pmic_typec_pdphy_pd_transmit(struct pmic_typec_pdphy *pmic_typec_pdphy, + enum tcpm_transmit_type type, + const struct pd_message *msg, + unsigned int negotiated_rev); + +#endif /* __QCOM_PMIC_TYPEC_PDPHY_H__ */ diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c new file mode 100644 index 000000000000..94285f64b67d --- /dev/null +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c @@ -0,0 +1,556 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023, Linaro Ltd. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qcom_pmic_typec_port.h" + +struct pmic_typec_port_irq_data { + int virq; + int irq; + struct pmic_typec_port *pmic_typec_port; +}; + +struct pmic_typec_port { + struct device *dev; + struct tcpm_port *tcpm_port; + struct regmap *regmap; + u32 base; + unsigned int nr_irqs; + struct pmic_typec_port_irq_data *irq_data; + + struct regulator *vdd_vbus; + + int cc; + bool debouncing_cc; + struct delayed_work cc_debounce_dwork; + + spinlock_t lock; /* Register atomicity */ +}; + +static const char * const typec_cc_status_name[] = { + [TYPEC_CC_OPEN] = "Open", + [TYPEC_CC_RA] = "Ra", + [TYPEC_CC_RD] = "Rd", + [TYPEC_CC_RP_DEF] = "Rp-def", + [TYPEC_CC_RP_1_5] = "Rp-1.5", + [TYPEC_CC_RP_3_0] = "Rp-3.0", +}; + +static const char *rp_unknown = "unknown"; + +static const char *cc_to_name(enum typec_cc_status cc) +{ + if (cc > TYPEC_CC_RP_3_0) + return rp_unknown; + + return typec_cc_status_name[cc]; +} + +static const char * const rp_sel_name[] = { + [TYPEC_SRC_RP_SEL_80UA] = "Rp-def-80uA", + [TYPEC_SRC_RP_SEL_180UA] = "Rp-1.5-180uA", + [TYPEC_SRC_RP_SEL_330UA] = "Rp-3.0-330uA", +}; + +static const char *rp_sel_to_name(int rp_sel) +{ + if (rp_sel > TYPEC_SRC_RP_SEL_330UA) + return rp_unknown; + + return rp_sel_name[rp_sel]; +} + +#define misc_to_cc(msic) !!(misc & CC_ORIENTATION) ? "cc1" : "cc2" +#define misc_to_vconn(msic) !!(misc & CC_ORIENTATION) ? "cc2" : "cc1" + +static void qcom_pmic_typec_port_cc_debounce(struct work_struct *work) +{ + struct pmic_typec_port *pmic_typec_port = + container_of(work, struct pmic_typec_port, cc_debounce_dwork.work); + unsigned long flags; + + spin_lock_irqsave(&pmic_typec_port->lock, flags); + pmic_typec_port->debouncing_cc = false; + spin_unlock_irqrestore(&pmic_typec_port->lock, flags); + + dev_dbg(pmic_typec_port->dev, "Debounce cc complete\n"); +} + +static irqreturn_t pmic_typec_port_isr(int irq, void *dev_id) +{ + struct pmic_typec_port_irq_data *irq_data = dev_id; + struct pmic_typec_port *pmic_typec_port = irq_data->pmic_typec_port; + u32 misc_stat; + bool vbus_change = false; + bool cc_change = false; + unsigned long flags; + int ret; + + spin_lock_irqsave(&pmic_typec_port->lock, flags); + + ret = regmap_read(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_MISC_STATUS_REG, + &misc_stat); + if (ret) + goto done; + + switch (irq_data->virq) { + case PMIC_TYPEC_VBUS_IRQ: + vbus_change = true; + break; + case PMIC_TYPEC_CC_STATE_IRQ: + case PMIC_TYPEC_ATTACH_DETACH_IRQ: + if (!pmic_typec_port->debouncing_cc) + cc_change = true; + break; + } + +done: + spin_unlock_irqrestore(&pmic_typec_port->lock, flags); + + if (vbus_change) + tcpm_vbus_change(pmic_typec_port->tcpm_port); + + if (cc_change) + tcpm_cc_change(pmic_typec_port->tcpm_port); + + return IRQ_HANDLED; +} + +int qcom_pmic_typec_port_get_vbus(struct pmic_typec_port *pmic_typec_port) +{ + struct device *dev = pmic_typec_port->dev; + unsigned int misc; + int ret; + + ret = regmap_read(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_MISC_STATUS_REG, + &misc); + if (ret) + misc = 0; + + dev_dbg(dev, "get_vbus: 0x%08x detect %d\n", misc, !!(misc & TYPEC_VBUS_DETECT)); + + return !!(misc & TYPEC_VBUS_DETECT); +} + +int qcom_pmic_typec_port_set_vbus(struct pmic_typec_port *pmic_typec_port, bool on) +{ + u32 sm_stat; + u32 val; + int ret; + + if (on) { + ret = regulator_enable(pmic_typec_port->vdd_vbus); + if (ret) + return ret; + + val = TYPEC_SM_VBUS_VSAFE5V; + } else { + ret = regulator_disable(pmic_typec_port->vdd_vbus); + if (ret) + return ret; + + val = TYPEC_SM_VBUS_VSAFE0V; + } + + /* Poll waiting for transition to required vSafe5V or vSafe0V */ + ret = regmap_read_poll_timeout(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_SM_STATUS_REG, + sm_stat, sm_stat & val, + 100, 250000); + if (ret) + dev_warn(pmic_typec_port->dev, "vbus vsafe%dv fail\n", on ? 5 : 0); + + return 0; +} + +int qcom_pmic_typec_port_get_cc(struct pmic_typec_port *pmic_typec_port, + enum typec_cc_status *cc1, + enum typec_cc_status *cc2) +{ + struct device *dev = pmic_typec_port->dev; + unsigned int misc, val; + bool attached; + int ret = 0; + + ret = regmap_read(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_MISC_STATUS_REG, &misc); + if (ret) + goto done; + + attached = !!(misc & CC_ATTACHED); + + if (pmic_typec_port->debouncing_cc) { + ret = -EBUSY; + goto done; + } + + *cc1 = TYPEC_CC_OPEN; + *cc2 = TYPEC_CC_OPEN; + + if (!attached) + goto done; + + if (misc & SNK_SRC_MODE) { + ret = regmap_read(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_SRC_STATUS_REG, + &val); + if (ret) + goto done; + switch (val & DETECTED_SRC_TYPE_MASK) { + case SRC_RD_OPEN: + val = TYPEC_CC_RD; + break; + case SRC_RD_RA_VCONN: + val = TYPEC_CC_RD; + *cc1 = TYPEC_CC_RA; + *cc2 = TYPEC_CC_RA; + break; + default: + dev_warn(dev, "unexpected src status %.2x\n", val); + val = TYPEC_CC_RD; + break; + } + } else { + ret = regmap_read(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_SNK_STATUS_REG, + &val); + if (ret) + goto done; + switch (val & DETECTED_SNK_TYPE_MASK) { + case SNK_RP_STD: + val = TYPEC_CC_RP_DEF; + break; + case SNK_RP_1P5: + val = TYPEC_CC_RP_1_5; + break; + case SNK_RP_3P0: + val = TYPEC_CC_RP_3_0; + break; + default: + dev_warn(dev, "unexpected snk status %.2x\n", val); + val = TYPEC_CC_RP_DEF; + break; + } + val = TYPEC_CC_RP_DEF; + } + + if (misc & CC_ORIENTATION) + *cc2 = val; + else + *cc1 = val; + +done: + dev_dbg(dev, "get_cc: misc 0x%08x cc1 0x%08x %s cc2 0x%08x %s attached %d cc=%s\n", + misc, *cc1, cc_to_name(*cc1), *cc2, cc_to_name(*cc2), attached, + misc_to_cc(misc)); + + return ret; +} + +static void qcom_pmic_set_cc_debounce(struct pmic_typec_port *pmic_typec_port) +{ + pmic_typec_port->debouncing_cc = true; + schedule_delayed_work(&pmic_typec_port->cc_debounce_dwork, + msecs_to_jiffies(2)); +} + +int qcom_pmic_typec_port_set_cc(struct pmic_typec_port *pmic_typec_port, + enum typec_cc_status cc) +{ + struct device *dev = pmic_typec_port->dev; + unsigned int mode, currsrc; + unsigned int misc; + unsigned long flags; + int ret; + + spin_lock_irqsave(&pmic_typec_port->lock, flags); + + ret = regmap_read(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_MISC_STATUS_REG, + &misc); + if (ret) + goto done; + + mode = EN_SRC_ONLY; + + switch (cc) { + case TYPEC_CC_OPEN: + currsrc = TYPEC_SRC_RP_SEL_80UA; + break; + case TYPEC_CC_RP_DEF: + currsrc = TYPEC_SRC_RP_SEL_80UA; + break; + case TYPEC_CC_RP_1_5: + currsrc = TYPEC_SRC_RP_SEL_180UA; + break; + case TYPEC_CC_RP_3_0: + currsrc = TYPEC_SRC_RP_SEL_330UA; + break; + case TYPEC_CC_RD: + currsrc = TYPEC_SRC_RP_SEL_80UA; + mode = EN_SNK_ONLY; + break; + default: + dev_warn(dev, "unexpected set_cc %d\n", cc); + ret = -EINVAL; + goto done; + } + + if (mode == EN_SRC_ONLY) { + ret = regmap_write(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_CURRSRC_CFG_REG, + currsrc); + if (ret) + goto done; + } + + pmic_typec_port->cc = cc; + qcom_pmic_set_cc_debounce(pmic_typec_port); + ret = 0; + +done: + spin_unlock_irqrestore(&pmic_typec_port->lock, flags); + + dev_dbg(dev, "set_cc: currsrc=%x %s mode %s debounce %d attached %d cc=%s\n", + currsrc, rp_sel_to_name(currsrc), + mode == EN_SRC_ONLY ? "EN_SRC_ONLY" : "EN_SNK_ONLY", + pmic_typec_port->debouncing_cc, !!(misc & CC_ATTACHED), + misc_to_cc(misc)); + + return ret; +} + +int qcom_pmic_typec_port_set_vconn(struct pmic_typec_port *pmic_typec_port, bool on) +{ + struct device *dev = pmic_typec_port->dev; + unsigned int orientation, misc, mask, value; + unsigned long flags; + int ret; + + spin_lock_irqsave(&pmic_typec_port->lock, flags); + + ret = regmap_read(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_MISC_STATUS_REG, &misc); + if (ret) + goto done; + + /* Set VCONN on the inversion of the active CC channel */ + orientation = (misc & CC_ORIENTATION) ? 0 : VCONN_EN_ORIENTATION; + if (on) { + mask = VCONN_EN_ORIENTATION | VCONN_EN_VALUE; + value = orientation | VCONN_EN_VALUE | VCONN_EN_SRC; + } else { + mask = VCONN_EN_VALUE; + value = 0; + } + + ret = regmap_update_bits(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_VCONN_CONTROL_REG, + mask, value); +done: + spin_unlock_irqrestore(&pmic_typec_port->lock, flags); + + dev_dbg(dev, "set_vconn: orientation %d control 0x%08x state %s cc %s vconn %s\n", + orientation, value, on ? "on" : "off", misc_to_vconn(misc), misc_to_cc(misc)); + + return ret; +} + +int qcom_pmic_typec_port_start_toggling(struct pmic_typec_port *pmic_typec_port, + enum typec_port_type port_type, + enum typec_cc_status cc) +{ + struct device *dev = pmic_typec_port->dev; + unsigned int misc; + u8 mode = 0; + unsigned long flags; + int ret; + + switch (port_type) { + case TYPEC_PORT_SRC: + mode = EN_SRC_ONLY; + break; + case TYPEC_PORT_SNK: + mode = EN_SNK_ONLY; + break; + case TYPEC_PORT_DRP: + mode = EN_TRY_SNK; + break; + } + + spin_lock_irqsave(&pmic_typec_port->lock, flags); + + ret = regmap_read(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_MISC_STATUS_REG, &misc); + if (ret) + goto done; + + dev_dbg(dev, "start_toggling: misc 0x%08x attached %d port_type %d current cc %d new %d\n", + misc, !!(misc & CC_ATTACHED), port_type, pmic_typec_port->cc, cc); + + qcom_pmic_set_cc_debounce(pmic_typec_port); + + /* force it to toggle at least once */ + ret = regmap_write(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_MODE_CFG_REG, + TYPEC_DISABLE_CMD); + if (ret) + goto done; + + ret = regmap_write(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_MODE_CFG_REG, + mode); +done: + spin_unlock_irqrestore(&pmic_typec_port->lock, flags); + + return ret; +} + +#define TYPEC_INTR_EN_CFG_1_MASK \ + (TYPEC_LEGACY_CABLE_INT_EN | \ + TYPEC_NONCOMPLIANT_LEGACY_CABLE_INT_EN | \ + TYPEC_TRYSOURCE_DETECT_INT_EN | \ + TYPEC_TRYSINK_DETECT_INT_EN | \ + TYPEC_CCOUT_DETACH_INT_EN | \ + TYPEC_CCOUT_ATTACH_INT_EN | \ + TYPEC_VBUS_DEASSERT_INT_EN | \ + TYPEC_VBUS_ASSERT_INT_EN) + +#define TYPEC_INTR_EN_CFG_2_MASK \ + (TYPEC_STATE_MACHINE_CHANGE_INT_EN | TYPEC_VBUS_ERROR_INT_EN | \ + TYPEC_DEBOUNCE_DONE_INT_EN) + +int qcom_pmic_typec_port_start(struct pmic_typec_port *pmic_typec_port, + struct tcpm_port *tcpm_port) +{ + int i; + int mask; + int ret; + + /* Configure interrupt sources */ + ret = regmap_write(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_INTERRUPT_EN_CFG_1_REG, + TYPEC_INTR_EN_CFG_1_MASK); + if (ret) + goto done; + + ret = regmap_write(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_INTERRUPT_EN_CFG_2_REG, + TYPEC_INTR_EN_CFG_2_MASK); + if (ret) + goto done; + + /* start in TRY_SNK mode */ + ret = regmap_write(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_MODE_CFG_REG, EN_TRY_SNK); + if (ret) + goto done; + + /* Configure VCONN for software control */ + ret = regmap_update_bits(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_VCONN_CONTROL_REG, + VCONN_EN_SRC | VCONN_EN_VALUE, VCONN_EN_SRC); + if (ret) + goto done; + + /* Set CC threshold to 1.6 Volts | tPDdebounce = 10-20ms */ + mask = SEL_SRC_UPPER_REF | USE_TPD_FOR_EXITING_ATTACHSRC; + ret = regmap_update_bits(pmic_typec_port->regmap, + pmic_typec_port->base + TYPEC_EXIT_STATE_CFG_REG, + mask, mask); + if (ret) + goto done; + + pmic_typec_port->tcpm_port = tcpm_port; + + for (i = 0; i < pmic_typec_port->nr_irqs; i++) + enable_irq(pmic_typec_port->irq_data[i].irq); + +done: + return ret; +} + +void qcom_pmic_typec_port_stop(struct pmic_typec_port *pmic_typec_port) +{ + int i; + + for (i = 0; i < pmic_typec_port->nr_irqs; i++) + disable_irq(pmic_typec_port->irq_data[i].irq); +} + +struct pmic_typec_port *qcom_pmic_typec_port_alloc(struct device *dev) +{ + return devm_kzalloc(dev, sizeof(struct pmic_typec_port), GFP_KERNEL); +} + +int qcom_pmic_typec_port_probe(struct platform_device *pdev, + struct pmic_typec_port *pmic_typec_port, + struct pmic_typec_port_resources *res, + struct regmap *regmap, + u32 base) +{ + struct device *dev = &pdev->dev; + struct pmic_typec_port_irq_data *irq_data; + int i, ret, irq; + + if (!res->nr_irqs || res->nr_irqs > PMIC_TYPEC_MAX_IRQS) + return -EINVAL; + + irq_data = devm_kzalloc(dev, sizeof(*irq_data) * res->nr_irqs, + GFP_KERNEL); + if (!irq_data) + return -ENOMEM; + + pmic_typec_port->vdd_vbus = devm_regulator_get(dev, "vdd-vbus"); + if (IS_ERR(pmic_typec_port->vdd_vbus)) + return PTR_ERR(pmic_typec_port->vdd_vbus); + + pmic_typec_port->dev = dev; + pmic_typec_port->base = base; + pmic_typec_port->regmap = regmap; + pmic_typec_port->nr_irqs = res->nr_irqs; + pmic_typec_port->irq_data = irq_data; + spin_lock_init(&pmic_typec_port->lock); + INIT_DELAYED_WORK(&pmic_typec_port->cc_debounce_dwork, + qcom_pmic_typec_port_cc_debounce); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + for (i = 0; i < res->nr_irqs; i++, irq_data++) { + irq = platform_get_irq_byname(pdev, + res->irq_params[i].irq_name); + if (irq < 0) + return irq; + + irq_data->pmic_typec_port = pmic_typec_port; + irq_data->irq = irq; + irq_data->virq = res->irq_params[i].virq; + ret = devm_request_threaded_irq(dev, irq, NULL, pmic_typec_port_isr, + IRQF_ONESHOT | IRQF_NO_AUTOEN, + res->irq_params[i].irq_name, + irq_data); + if (ret) + return ret; + } + + return 0; +} diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.h b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.h new file mode 100644 index 000000000000..d4d358c680b6 --- /dev/null +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.h @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2023, Linaro Ltd. All rights reserved. + */ +#ifndef __QCOM_PMIC_TYPEC_H__ +#define __QCOM_PMIC_TYPEC_H__ + +#include +#include + +#define TYPEC_SNK_STATUS_REG 0x06 +#define DETECTED_SNK_TYPE_MASK GENMASK(6, 0) +#define SNK_DAM_MASK GENMASK(6, 4) +#define SNK_DAM_500MA BIT(6) +#define SNK_DAM_1500MA BIT(5) +#define SNK_DAM_3000MA BIT(4) +#define SNK_RP_STD BIT(3) +#define SNK_RP_1P5 BIT(2) +#define SNK_RP_3P0 BIT(1) +#define SNK_RP_SHORT BIT(0) + +#define TYPEC_SRC_STATUS_REG 0x08 +#define DETECTED_SRC_TYPE_MASK GENMASK(4, 0) +#define SRC_HIGH_BATT BIT(5) +#define SRC_DEBUG_ACCESS BIT(4) +#define SRC_RD_OPEN BIT(3) +#define SRC_RD_RA_VCONN BIT(2) +#define SRC_RA_OPEN BIT(1) +#define AUDIO_ACCESS_RA_RA BIT(0) + +#define TYPEC_STATE_MACHINE_STATUS_REG 0x09 +#define TYPEC_ATTACH_DETACH_STATE BIT(5) + +#define TYPEC_SM_STATUS_REG 0x0A +#define TYPEC_SM_VBUS_VSAFE5V BIT(5) +#define TYPEC_SM_VBUS_VSAFE0V BIT(6) +#define TYPEC_SM_USBIN_LT_LV BIT(7) + +#define TYPEC_MISC_STATUS_REG 0x0B +#define TYPEC_WATER_DETECTION_STATUS BIT(7) +#define SNK_SRC_MODE BIT(6) +#define TYPEC_VBUS_DETECT BIT(5) +#define TYPEC_VBUS_ERROR_STATUS BIT(4) +#define TYPEC_DEBOUNCE_DONE BIT(3) +#define CC_ORIENTATION BIT(1) +#define CC_ATTACHED BIT(0) + +#define LEGACY_CABLE_STATUS_REG 0x0D +#define TYPEC_LEGACY_CABLE_STATUS BIT(1) +#define TYPEC_NONCOMP_LEGACY_CABLE_STATUS BIT(0) + +#define TYPEC_U_USB_STATUS_REG 0x0F +#define U_USB_GROUND_NOVBUS BIT(6) +#define U_USB_GROUND BIT(4) +#define U_USB_FMB1 BIT(3) +#define U_USB_FLOAT1 BIT(2) +#define U_USB_FMB2 BIT(1) +#define U_USB_FLOAT2 BIT(0) + +#define TYPEC_MODE_CFG_REG 0x44 +#define TYPEC_TRY_MODE_MASK GENMASK(4, 3) +#define EN_TRY_SNK BIT(4) +#define EN_TRY_SRC BIT(3) +#define TYPEC_POWER_ROLE_CMD_MASK GENMASK(2, 0) +#define EN_SRC_ONLY BIT(2) +#define EN_SNK_ONLY BIT(1) +#define TYPEC_DISABLE_CMD BIT(0) + +#define TYPEC_VCONN_CONTROL_REG 0x46 +#define VCONN_EN_ORIENTATION BIT(2) +#define VCONN_EN_VALUE BIT(1) +#define VCONN_EN_SRC BIT(0) + +#define TYPEC_CCOUT_CONTROL_REG 0x48 +#define TYPEC_CCOUT_BUFFER_EN BIT(2) +#define TYPEC_CCOUT_VALUE BIT(1) +#define TYPEC_CCOUT_SRC BIT(0) + +#define DEBUG_ACCESS_SRC_CFG_REG 0x4C +#define EN_UNORIENTED_DEBUG_ACCESS_SRC BIT(0) + +#define TYPE_C_CRUDE_SENSOR_CFG_REG 0x4e +#define EN_SRC_CRUDE_SENSOR BIT(1) +#define EN_SNK_CRUDE_SENSOR BIT(0) + +#define TYPEC_EXIT_STATE_CFG_REG 0x50 +#define BYPASS_VSAFE0V_DURING_ROLE_SWAP BIT(3) +#define SEL_SRC_UPPER_REF BIT(2) +#define USE_TPD_FOR_EXITING_ATTACHSRC BIT(1) +#define EXIT_SNK_BASED_ON_CC BIT(0) + +#define TYPEC_CURRSRC_CFG_REG 0x52 +#define TYPEC_SRC_RP_SEL_330UA BIT(1) +#define TYPEC_SRC_RP_SEL_180UA BIT(0) +#define TYPEC_SRC_RP_SEL_80UA 0 +#define TYPEC_SRC_RP_SEL_MASK GENMASK(1, 0) + +#define TYPEC_INTERRUPT_EN_CFG_1_REG 0x5E +#define TYPEC_LEGACY_CABLE_INT_EN BIT(7) +#define TYPEC_NONCOMPLIANT_LEGACY_CABLE_INT_EN BIT(6) +#define TYPEC_TRYSOURCE_DETECT_INT_EN BIT(5) +#define TYPEC_TRYSINK_DETECT_INT_EN BIT(4) +#define TYPEC_CCOUT_DETACH_INT_EN BIT(3) +#define TYPEC_CCOUT_ATTACH_INT_EN BIT(2) +#define TYPEC_VBUS_DEASSERT_INT_EN BIT(1) +#define TYPEC_VBUS_ASSERT_INT_EN BIT(0) + +#define TYPEC_INTERRUPT_EN_CFG_2_REG 0x60 +#define TYPEC_SRC_BATT_HPWR_INT_EN BIT(6) +#define MICRO_USB_STATE_CHANGE_INT_EN BIT(5) +#define TYPEC_STATE_MACHINE_CHANGE_INT_EN BIT(4) +#define TYPEC_DEBUG_ACCESS_DETECT_INT_EN BIT(3) +#define TYPEC_WATER_DETECTION_INT_EN BIT(2) +#define TYPEC_VBUS_ERROR_INT_EN BIT(1) +#define TYPEC_DEBOUNCE_DONE_INT_EN BIT(0) + +#define TYPEC_DEBOUNCE_OPTION_REG 0x62 +#define REDUCE_TCCDEBOUNCE_TO_2MS BIT(2) + +#define TYPE_C_SBU_CFG_REG 0x6A +#define SEL_SBU1_ISRC_VAL 0x04 +#define SEL_SBU2_ISRC_VAL 0x01 + +#define TYPEC_U_USB_CFG_REG 0x70 +#define EN_MICRO_USB_FACTORY_MODE BIT(1) +#define EN_MICRO_USB_MODE BIT(0) + +#define TYPEC_PMI632_U_USB_WATER_PROTECTION_CFG_REG 0x72 + +#define TYPEC_U_USB_WATER_PROTECTION_CFG_REG 0x73 +#define EN_MICRO_USB_WATER_PROTECTION BIT(4) +#define MICRO_USB_DETECTION_ON_TIME_CFG_MASK GENMASK(3, 2) +#define MICRO_USB_DETECTION_PERIOD_CFG_MASK GENMASK(1, 0) + +#define TYPEC_PMI632_MICRO_USB_MODE_REG 0x73 +#define MICRO_USB_MODE_ONLY BIT(0) + +/* Interrupt numbers */ +#define PMIC_TYPEC_OR_RID_IRQ 0x0 +#define PMIC_TYPEC_VPD_IRQ 0x1 +#define PMIC_TYPEC_CC_STATE_IRQ 0x2 +#define PMIC_TYPEC_VCONN_OC_IRQ 0x3 +#define PMIC_TYPEC_VBUS_IRQ 0x4 +#define PMIC_TYPEC_ATTACH_DETACH_IRQ 0x5 +#define PMIC_TYPEC_LEGACY_CABLE_IRQ 0x6 +#define PMIC_TYPEC_TRY_SNK_SRC_IRQ 0x7 + +/* Resources */ +#define PMIC_TYPEC_MAX_IRQS 0x08 + +struct pmic_typec_port_irq_params { + int virq; + char *irq_name; +}; + +struct pmic_typec_port_resources { + unsigned int nr_irqs; + struct pmic_typec_port_irq_params irq_params[PMIC_TYPEC_MAX_IRQS]; +}; + +/* API */ +struct pmic_typec; + +struct pmic_typec_port *qcom_pmic_typec_port_alloc(struct device *dev); + +int qcom_pmic_typec_port_probe(struct platform_device *pdev, + struct pmic_typec_port *pmic_typec_port, + struct pmic_typec_port_resources *res, + struct regmap *regmap, + u32 base); + +int qcom_pmic_typec_port_start(struct pmic_typec_port *pmic_typec_port, + struct tcpm_port *tcpm_port); + +void qcom_pmic_typec_port_stop(struct pmic_typec_port *pmic_typec_port); + +int qcom_pmic_typec_port_get_cc(struct pmic_typec_port *pmic_typec_port, + enum typec_cc_status *cc1, + enum typec_cc_status *cc2); + +int qcom_pmic_typec_port_set_cc(struct pmic_typec_port *pmic_typec_port, + enum typec_cc_status cc); + +int qcom_pmic_typec_port_get_vbus(struct pmic_typec_port *pmic_typec_port); + +int qcom_pmic_typec_port_set_vconn(struct pmic_typec_port *pmic_typec_port, bool on); + +int qcom_pmic_typec_port_start_toggling(struct pmic_typec_port *pmic_typec_port, + enum typec_port_type port_type, + enum typec_cc_status cc); + +int qcom_pmic_typec_port_set_vbus(struct pmic_typec_port *pmic_typec_port, bool on); + +#endif /* __QCOM_PMIC_TYPE_C_PORT_H__ */ -- cgit v1.2.3 From ee4d21aa4a227466c5635831e950c7276db797db Mon Sep 17 00:00:00 2001 From: Lukas Bulwahn Date: Fri, 19 May 2023 06:13:07 +0200 Subject: MAINTAINERS: remove broken entries in QUALCOMM TYPEC PORT MANAGER DRIVER Commit a4422ff22142 ("usb: typec: qcom: Add Qualcomm PMIC Type-C driver") adds the section QUALCOMM TYPEC PORT MANAGER DRIVER in MAINTAINERS with two file entries for header files in include/dt-bindings/usb/typec/. However, these files are not added to the repository with this commit or any commit in the related patch series. Probably, these file entries are just needless leftover after the work went through some refactoring. Hence, ./scripts/get_maintainer.pl --self-test=patterns complains about a broken reference. Remove the two file entries for non-existent header files. Signed-off-by: Lukas Bulwahn Reviewed-by: Bryan O'Donoghue Link: https://lore.kernel.org/r/20230519041307.32322-1-lukas.bulwahn@gmail.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 2 -- 1 file changed, 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index b5424199c32d..0aaf67179f8c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17540,8 +17540,6 @@ L: linux-usb@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/usb/qcom,pmic-*.yaml F: drivers/usb/typec/tcpm/qcom/ -F: include/dt-bindings/usb/typec/qcom,pmic-pdphy.h -F: include/dt-bindings/usb/typec/qcom,pmic-typec.h QUALCOMM VENUS VIDEO ACCELERATOR DRIVER M: Stanimir Varbanov -- cgit v1.2.3 From 814c96c959cfc147ecf8bf9ae3f34b8361316316 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 31 Mar 2023 13:01:32 +0300 Subject: thunderbolt: Check for ring 0 in tb_tunnel_alloc_dma() Ring 0 cannot be used for anything else than control channel messages. For this reason add a check to tb_tunnel_alloc_dma() and fail if someone tries to do that. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tunnel.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 9099ae73e78f..dd3b5613ad2c 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -1452,6 +1452,10 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, struct tb_path *path; int credits; + /* Ring 0 is reserved for control channel */ + if (WARN_ON(!receive_ring || !transmit_ring)) + return NULL; + if (receive_ring > 0) npaths++; if (transmit_ring > 0) -- cgit v1.2.3 From f14d177e0be652ef7b265753f08f2a7d31935668 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 3 Feb 2023 15:57:59 +0200 Subject: thunderbolt: Log function name of the called quirk This is useful when debugging whether a quirk has been matched or not. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/quirks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/thunderbolt/quirks.c b/drivers/thunderbolt/quirks.c index 1157b8869bcc..928689b66126 100644 --- a/drivers/thunderbolt/quirks.c +++ b/drivers/thunderbolt/quirks.c @@ -105,6 +105,7 @@ void tb_check_quirks(struct tb_switch *sw) if (q->device && q->device != sw->device) continue; + tb_sw_dbg(sw, "running %ps\n", q->hook); q->hook(sw); } } -- cgit v1.2.3 From ccdb0900a0c3b0b56af5f547cceb64ee8d09483f Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 27 Feb 2023 12:45:09 +0200 Subject: thunderbolt: Add debug log for link controller power quirk Add a debug log to this quirk as well so we can see what quirks have been applied when debugging. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/quirks.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/thunderbolt/quirks.c b/drivers/thunderbolt/quirks.c index 928689b66126..854d84148850 100644 --- a/drivers/thunderbolt/quirks.c +++ b/drivers/thunderbolt/quirks.c @@ -10,6 +10,7 @@ static void quirk_force_power_link(struct tb_switch *sw) { sw->quirks |= QUIRK_FORCE_POWER_LINK_CONTROLLER; + tb_sw_dbg(sw, "forcing power to link controller\n"); } static void quirk_dp_credit_allocation(struct tb_switch *sw) -- cgit v1.2.3 From 7ee20d0afb69dfc79ef50d184d4b571f9064f9ba Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 8 Sep 2022 13:17:26 +0300 Subject: thunderbolt: Allow specifying custom credits for DMA tunnels The default ones should be find but this allows the user to tweak the credits to get more performance out of the P2P connection. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tunnel.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index dd3b5613ad2c..3bf2628a5dcd 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -41,9 +41,14 @@ * Number of credits we try to allocate for each DMA path if not limited * by the host router baMaxHI. */ -#define TB_DMA_CREDITS 14U +#define TB_DMA_CREDITS 14 /* Minimum number of credits for DMA path */ -#define TB_MIN_DMA_CREDITS 1U +#define TB_MIN_DMA_CREDITS 1 + +static unsigned int dma_credits = TB_DMA_CREDITS; +module_param(dma_credits, uint, 0444); +MODULE_PARM_DESC(dma_credits, "specify custom credits for DMA tunnels (default: " + __MODULE_STRING(TB_DMA_CREDITS) ")"); static bool bw_alloc_mode = true; module_param(bw_alloc_mode, bool, 0444); @@ -95,7 +100,7 @@ static unsigned int tb_available_credits(const struct tb_port *port, pcie = tb_acpi_may_tunnel_pcie() ? sw->max_pcie_credits : 0; if (tb_acpi_is_xdomain_allowed()) { - spare = min_not_zero(sw->max_dma_credits, TB_DMA_CREDITS); + spare = min_not_zero(sw->max_dma_credits, dma_credits); /* Add some credits for potential second DMA tunnel */ spare += TB_MIN_DMA_CREDITS; } else { @@ -1472,7 +1477,7 @@ struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi, tunnel->dst_port = dst; tunnel->deinit = tb_dma_deinit; - credits = min_not_zero(TB_DMA_CREDITS, nhi->sw->max_dma_credits); + credits = min_not_zero(dma_credits, nhi->sw->max_dma_credits); if (receive_ring > 0) { path = tb_path_alloc(tb, dst, receive_path, nhi, receive_ring, 0, -- cgit v1.2.3 From 714e57aa3bcd9c35b856343d68e82ae0cab4e181 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 24 Mar 2023 20:43:49 +0200 Subject: thunderbolt: Add MODULE_DESCRIPTION Add description about the driver to the module. No functional changes. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/nhi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index c0aee5dc5237..565892a2cdb9 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -1483,6 +1483,7 @@ static struct pci_device_id nhi_ids[] = { }; MODULE_DEVICE_TABLE(pci, nhi_ids); +MODULE_DESCRIPTION("Thunderbolt/USB4 core driver"); MODULE_LICENSE("GPL"); static struct pci_driver nhi_driver = { -- cgit v1.2.3 From 88a9ded93453dea6748572242faf30f199be4f41 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 24 Mar 2023 20:44:56 +0200 Subject: thunderbolt: dma_test: Update MODULE_DESCRIPTION Make the description match the core driver and the networking with Thunderbolt/USB4 prefix. No functional changes. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/dma_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/dma_test.c b/drivers/thunderbolt/dma_test.c index 3bedecb236e0..c29cf620ee7d 100644 --- a/drivers/thunderbolt/dma_test.c +++ b/drivers/thunderbolt/dma_test.c @@ -756,5 +756,5 @@ module_exit(dma_test_exit); MODULE_AUTHOR("Isaac Hazan "); MODULE_AUTHOR("Mika Westerberg "); -MODULE_DESCRIPTION("DMA traffic test driver"); +MODULE_DESCRIPTION("Thunderbolt/USB4 DMA traffic test driver"); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From f1138fda1b0db43ff63d19923f8e84951c0c4d1c Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 24 Mar 2023 18:30:00 +0200 Subject: thunderbolt: Drop retimer vendor check This is not needed anymore as we already handle unknown vendor in NVM functions. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/retimer.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c index 9cc28197dbc4..ccc2f0e7adba 100644 --- a/drivers/thunderbolt/retimer.c +++ b/drivers/thunderbolt/retimer.c @@ -341,12 +341,6 @@ static int tb_retimer_add(struct tb_port *port, u8 index, u32 auth_status) return ret; } - if (vendor != PCI_VENDOR_ID_INTEL && vendor != 0x8087) { - tb_port_info(port, "retimer NVM format of vendor %#x is not supported\n", - vendor); - return -EOPNOTSUPP; - } - /* * Check that it supports NVM operations. If not then don't add * the device at all. -- cgit v1.2.3 From e8fa2dd9e2bc12f8386f3d5b897d8ea025ee8b7b Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:03 +0200 Subject: usb: c67x00-drv: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-2-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/c67x00/c67x00-drv.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/c67x00/c67x00-drv.c b/drivers/usb/c67x00/c67x00-drv.c index 6db5cb1b2dbb..bb9d5d7ffefc 100644 --- a/drivers/usb/c67x00/c67x00-drv.c +++ b/drivers/usb/c67x00/c67x00-drv.c @@ -177,7 +177,7 @@ static int c67x00_drv_probe(struct platform_device *pdev) return ret; } -static int c67x00_drv_remove(struct platform_device *pdev) +static void c67x00_drv_remove(struct platform_device *pdev) { struct c67x00_device *c67x00 = platform_get_drvdata(pdev); struct resource *res; @@ -197,13 +197,11 @@ static int c67x00_drv_remove(struct platform_device *pdev) release_mem_region(res->start, resource_size(res)); kfree(c67x00); - - return 0; } static struct platform_driver c67x00_driver = { .probe = c67x00_drv_probe, - .remove = c67x00_drv_remove, + .remove_new = c67x00_drv_remove, .driver = { .name = "c67x00", }, -- cgit v1.2.3 From cfab1b8be9ab744d4b1064a8af59c758a2084b1d Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:04 +0200 Subject: usb: cdns3-imx: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-3-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-imx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c index 59860d1753fd..e5930f894315 100644 --- a/drivers/usb/cdns3/cdns3-imx.c +++ b/drivers/usb/cdns3/cdns3-imx.c @@ -218,7 +218,7 @@ err: return ret; } -static int cdns_imx_remove(struct platform_device *pdev) +static void cdns_imx_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct cdns_imx *data = dev_get_drvdata(dev); @@ -229,8 +229,6 @@ static int cdns_imx_remove(struct platform_device *pdev) pm_runtime_disable(dev); pm_runtime_put_noidle(dev); platform_set_drvdata(pdev, NULL); - - return 0; } #ifdef CONFIG_PM @@ -416,7 +414,7 @@ MODULE_DEVICE_TABLE(of, cdns_imx_of_match); static struct platform_driver cdns_imx_driver = { .probe = cdns_imx_probe, - .remove = cdns_imx_remove, + .remove_new = cdns_imx_remove, .driver = { .name = "cdns3-imx", .of_match_table = cdns_imx_of_match, -- cgit v1.2.3 From 5411fa0ec65626180e3eddeb30633fc5e2313c01 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:05 +0200 Subject: usb: cdns3-plat: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-4-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-plat.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-plat.c b/drivers/usb/cdns3/cdns3-plat.c index 2bc5d094548b..884e2301237f 100644 --- a/drivers/usb/cdns3/cdns3-plat.c +++ b/drivers/usb/cdns3/cdns3-plat.c @@ -175,7 +175,7 @@ err_phy3_init: * * Returns 0 on success otherwise negative errno */ -static int cdns3_plat_remove(struct platform_device *pdev) +static void cdns3_plat_remove(struct platform_device *pdev) { struct cdns *cdns = platform_get_drvdata(pdev); struct device *dev = cdns->dev; @@ -187,7 +187,6 @@ static int cdns3_plat_remove(struct platform_device *pdev) set_phy_power_off(cdns); phy_exit(cdns->usb2_phy); phy_exit(cdns->usb3_phy); - return 0; } #ifdef CONFIG_PM @@ -320,7 +319,7 @@ MODULE_DEVICE_TABLE(of, of_cdns3_match); static struct platform_driver cdns3_driver = { .probe = cdns3_plat_probe, - .remove = cdns3_plat_remove, + .remove_new = cdns3_plat_remove, .driver = { .name = "cdns-usb3", .of_match_table = of_match_ptr(of_cdns3_match), -- cgit v1.2.3 From defbfca6945ec5f3362e15ee4ec15180117240c3 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:06 +0200 Subject: usb: cdns3-ti: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-5-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-ti.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-ti.c b/drivers/usb/cdns3/cdns3-ti.c index 07c318770362..81b9132e3aaa 100644 --- a/drivers/usb/cdns3/cdns3-ti.c +++ b/drivers/usb/cdns3/cdns3-ti.c @@ -199,7 +199,7 @@ static int cdns_ti_remove_core(struct device *dev, void *c) return 0; } -static int cdns_ti_remove(struct platform_device *pdev) +static void cdns_ti_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -208,8 +208,6 @@ static int cdns_ti_remove(struct platform_device *pdev) pm_runtime_disable(dev); platform_set_drvdata(pdev, NULL); - - return 0; } static const struct of_device_id cdns_ti_of_match[] = { @@ -221,7 +219,7 @@ MODULE_DEVICE_TABLE(of, cdns_ti_of_match); static struct platform_driver cdns_ti_driver = { .probe = cdns_ti_probe, - .remove = cdns_ti_remove, + .remove_new = cdns_ti_remove, .driver = { .name = "cdns3-ti", .of_match_table = cdns_ti_of_match, -- cgit v1.2.3 From ad593ed671feb49e93a77653886c042f68b6cdfd Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:07 +0200 Subject: usb: chipidea/ci_hdrc_imx: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-6-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_imx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 2855ac303001..9f0f4ec701c5 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -502,7 +502,7 @@ disable_hsic_regulator: return ret; } -static int ci_hdrc_imx_remove(struct platform_device *pdev) +static void ci_hdrc_imx_remove(struct platform_device *pdev) { struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev); @@ -522,8 +522,6 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev) if (data->hsic_pad_regulator) regulator_disable(data->hsic_pad_regulator); } - - return 0; } static void ci_hdrc_imx_shutdown(struct platform_device *pdev) @@ -650,7 +648,7 @@ static const struct dev_pm_ops ci_hdrc_imx_pm_ops = { }; static struct platform_driver ci_hdrc_imx_driver = { .probe = ci_hdrc_imx_probe, - .remove = ci_hdrc_imx_remove, + .remove_new = ci_hdrc_imx_remove, .shutdown = ci_hdrc_imx_shutdown, .driver = { .name = "imx_usb", -- cgit v1.2.3 From 1c74875cc5dbadd80e4bdadeba37f7d97e7a9c38 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:08 +0200 Subject: usb: chipidea/ci_hdrc_msm: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-7-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_msm.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c index 46105457e1ca..7b5b47ce8a02 100644 --- a/drivers/usb/chipidea/ci_hdrc_msm.c +++ b/drivers/usb/chipidea/ci_hdrc_msm.c @@ -274,7 +274,7 @@ err_iface: return ret; } -static int ci_hdrc_msm_remove(struct platform_device *pdev) +static void ci_hdrc_msm_remove(struct platform_device *pdev) { struct ci_hdrc_msm *ci = platform_get_drvdata(pdev); @@ -282,8 +282,6 @@ static int ci_hdrc_msm_remove(struct platform_device *pdev) ci_hdrc_remove_device(ci->ci); clk_disable_unprepare(ci->iface_clk); clk_disable_unprepare(ci->core_clk); - - return 0; } static const struct of_device_id msm_ci_dt_match[] = { @@ -294,7 +292,7 @@ MODULE_DEVICE_TABLE(of, msm_ci_dt_match); static struct platform_driver ci_hdrc_msm_driver = { .probe = ci_hdrc_msm_probe, - .remove = ci_hdrc_msm_remove, + .remove_new = ci_hdrc_msm_remove, .driver = { .name = "msm_hsusb", .of_match_table = msm_ci_dt_match, -- cgit v1.2.3 From 906ede9c779e69ce0b6cef4b750b93c7f5c69cf1 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:09 +0200 Subject: usb: chipidea/ci_hdrc_tegra: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-8-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_tegra.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c index a72a9474afea..ca36d11a69ea 100644 --- a/drivers/usb/chipidea/ci_hdrc_tegra.c +++ b/drivers/usb/chipidea/ci_hdrc_tegra.c @@ -362,7 +362,7 @@ fail_power_off: return err; } -static int tegra_usb_remove(struct platform_device *pdev) +static void tegra_usb_remove(struct platform_device *pdev) { struct tegra_usb *usb = platform_get_drvdata(pdev); @@ -371,8 +371,6 @@ static int tegra_usb_remove(struct platform_device *pdev) pm_runtime_put_sync_suspend(&pdev->dev); pm_runtime_force_suspend(&pdev->dev); - - return 0; } static int __maybe_unused tegra_usb_runtime_resume(struct device *dev) @@ -410,7 +408,7 @@ static struct platform_driver tegra_usb_driver = { .pm = &tegra_usb_pm, }, .probe = tegra_usb_probe, - .remove = tegra_usb_remove, + .remove_new = tegra_usb_remove, }; module_platform_driver(tegra_usb_driver); -- cgit v1.2.3 From 87202eae1daab035fb0d1a3c71cdc6f35091556c Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:10 +0200 Subject: usb: chipidea/ci_hdrc_usb2: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-9-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_usb2.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c index dc86b12060b5..1321ee67f3b8 100644 --- a/drivers/usb/chipidea/ci_hdrc_usb2.c +++ b/drivers/usb/chipidea/ci_hdrc_usb2.c @@ -106,20 +106,18 @@ clk_err: return ret; } -static int ci_hdrc_usb2_remove(struct platform_device *pdev) +static void ci_hdrc_usb2_remove(struct platform_device *pdev) { struct ci_hdrc_usb2_priv *priv = platform_get_drvdata(pdev); pm_runtime_disable(&pdev->dev); ci_hdrc_remove_device(priv->ci_pdev); clk_disable_unprepare(priv->clk); - - return 0; } static struct platform_driver ci_hdrc_usb2_driver = { .probe = ci_hdrc_usb2_probe, - .remove = ci_hdrc_usb2_remove, + .remove_new = ci_hdrc_usb2_remove, .driver = { .name = "chipidea-usb2", .of_match_table = of_match_ptr(ci_hdrc_usb2_of_match), -- cgit v1.2.3 From 49e71736dac7124175f30b9c6e696d3c9b1067fa Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:11 +0200 Subject: usb: chipidea/core: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-10-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 798cb077867a..51994d655b82 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -1227,7 +1227,7 @@ ulpi_exit: return ret; } -static int ci_hdrc_remove(struct platform_device *pdev) +static void ci_hdrc_remove(struct platform_device *pdev) { struct ci_hdrc *ci = platform_get_drvdata(pdev); @@ -1245,8 +1245,6 @@ static int ci_hdrc_remove(struct platform_device *pdev) ci_hdrc_enter_lpm(ci, true); ci_usb_phy_exit(ci); ci_ulpi_exit(ci); - - return 0; } #ifdef CONFIG_PM @@ -1485,7 +1483,7 @@ static const struct dev_pm_ops ci_pm_ops = { static struct platform_driver ci_hdrc_driver = { .probe = ci_hdrc_probe, - .remove = ci_hdrc_remove, + .remove_new = ci_hdrc_remove, .driver = { .name = "ci_hdrc", .pm = &ci_pm_ops, -- cgit v1.2.3 From 4f5bcf19bd2ab8ac4c94bb39951f43e0c5ecd2b0 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:12 +0200 Subject: usb: common: usb-conn-gpio: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-11-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/common/usb-conn-gpio.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c index e20874caba36..766005d20bae 100644 --- a/drivers/usb/common/usb-conn-gpio.c +++ b/drivers/usb/common/usb-conn-gpio.c @@ -267,7 +267,7 @@ put_role_sw: return ret; } -static int usb_conn_remove(struct platform_device *pdev) +static void usb_conn_remove(struct platform_device *pdev) { struct usb_conn_info *info = platform_get_drvdata(pdev); @@ -277,8 +277,6 @@ static int usb_conn_remove(struct platform_device *pdev) regulator_disable(info->vbus); usb_role_switch_put(info->role_sw); - - return 0; } static int __maybe_unused usb_conn_suspend(struct device *dev) @@ -338,7 +336,7 @@ MODULE_DEVICE_TABLE(of, usb_conn_dt_match); static struct platform_driver usb_conn_driver = { .probe = usb_conn_probe, - .remove = usb_conn_remove, + .remove_new = usb_conn_remove, .driver = { .name = "usb-conn-gpio", .pm = &usb_conn_pm_ops, -- cgit v1.2.3 From 0176568702a554effa493de04f51cbe7e4862ef2 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:14 +0200 Subject: usb: core: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20230517230239.187727-13-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 278cd1c33841..34c9a9a3bb79 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1924,7 +1924,7 @@ err_put_psy: return ret; } -static int dwc3_remove(struct platform_device *pdev) +static void dwc3_remove(struct platform_device *pdev) { struct dwc3 *dwc = platform_get_drvdata(pdev); @@ -1946,8 +1946,6 @@ static int dwc3_remove(struct platform_device *pdev) if (dwc->usb_psy) power_supply_put(dwc->usb_psy); - - return 0; } #ifdef CONFIG_PM @@ -2258,7 +2256,7 @@ MODULE_DEVICE_TABLE(acpi, dwc3_acpi_match); static struct platform_driver dwc3_driver = { .probe = dwc3_probe, - .remove = dwc3_remove, + .remove_new = dwc3_remove, .driver = { .name = "dwc3", .of_match_table = of_match_ptr(of_dwc3_match), -- cgit v1.2.3 From 890258e22117ed2378d1afc20a351cb46df3e787 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:15 +0200 Subject: usb: dwc3-am62: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20230517230239.187727-14-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-am62.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c index cda9458c809b..1755f2f848c5 100644 --- a/drivers/usb/dwc3/dwc3-am62.c +++ b/drivers/usb/dwc3/dwc3-am62.c @@ -275,7 +275,7 @@ static int dwc3_ti_remove_core(struct device *dev, void *c) return 0; } -static int dwc3_ti_remove(struct platform_device *pdev) +static void dwc3_ti_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct dwc3_data *data = platform_get_drvdata(pdev); @@ -294,7 +294,6 @@ static int dwc3_ti_remove(struct platform_device *pdev) pm_runtime_set_suspended(dev); platform_set_drvdata(pdev, NULL); - return 0; } #ifdef CONFIG_PM @@ -362,7 +361,7 @@ MODULE_DEVICE_TABLE(of, dwc3_ti_of_match); static struct platform_driver dwc3_ti_driver = { .probe = dwc3_ti_probe, - .remove = dwc3_ti_remove, + .remove_new = dwc3_ti_remove, .driver = { .name = "dwc3-am62", .pm = DEV_PM_OPS, -- cgit v1.2.3 From 8257d5f548d6212e62a6d4230a04a5db6d01b487 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:16 +0200 Subject: usb: dwc3-exynos: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20230517230239.187727-15-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-exynos.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c index 4be6a873bd07..f882dd647340 100644 --- a/drivers/usb/dwc3/dwc3-exynos.c +++ b/drivers/usb/dwc3/dwc3-exynos.c @@ -128,7 +128,7 @@ vdd33_err: return ret; } -static int dwc3_exynos_remove(struct platform_device *pdev) +static void dwc3_exynos_remove(struct platform_device *pdev) { struct dwc3_exynos *exynos = platform_get_drvdata(pdev); int i; @@ -143,8 +143,6 @@ static int dwc3_exynos_remove(struct platform_device *pdev) regulator_disable(exynos->vdd33); regulator_disable(exynos->vdd10); - - return 0; } static const struct dwc3_exynos_driverdata exynos5250_drvdata = { @@ -234,7 +232,7 @@ static const struct dev_pm_ops dwc3_exynos_dev_pm_ops = { static struct platform_driver dwc3_exynos_driver = { .probe = dwc3_exynos_probe, - .remove = dwc3_exynos_remove, + .remove_new = dwc3_exynos_remove, .driver = { .name = "exynos-dwc3", .of_match_table = exynos_dwc3_match, -- cgit v1.2.3 From 3791a3e6f45552bfae3695dc6a5f4b4f237ffca1 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:17 +0200 Subject: usb: dwc3-imx8mp: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20230517230239.187727-16-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-imx8mp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-imx8mp.c b/drivers/usb/dwc3/dwc3-imx8mp.c index 174f07614318..8b9a3bb587bf 100644 --- a/drivers/usb/dwc3/dwc3-imx8mp.c +++ b/drivers/usb/dwc3/dwc3-imx8mp.c @@ -266,7 +266,7 @@ disable_hsio_clk: return err; } -static int dwc3_imx8mp_remove(struct platform_device *pdev) +static void dwc3_imx8mp_remove(struct platform_device *pdev) { struct dwc3_imx8mp *dwc3_imx = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; @@ -280,8 +280,6 @@ static int dwc3_imx8mp_remove(struct platform_device *pdev) pm_runtime_disable(dev); pm_runtime_put_noidle(dev); platform_set_drvdata(pdev, NULL); - - return 0; } static int __maybe_unused dwc3_imx8mp_suspend(struct dwc3_imx8mp *dwc3_imx, @@ -411,7 +409,7 @@ MODULE_DEVICE_TABLE(of, dwc3_imx8mp_of_match); static struct platform_driver dwc3_imx8mp_driver = { .probe = dwc3_imx8mp_probe, - .remove = dwc3_imx8mp_remove, + .remove_new = dwc3_imx8mp_remove, .driver = { .name = "imx8mp-dwc3", .pm = &dwc3_imx8mp_dev_pm_ops, -- cgit v1.2.3 From 039e3dede538e59432badcd3cdd7d7d808c87efd Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:18 +0200 Subject: usb: dwc3-keystone: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20230517230239.187727-17-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-keystone.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c index 1317959294e6..0a09aedc2573 100644 --- a/drivers/usb/dwc3/dwc3-keystone.c +++ b/drivers/usb/dwc3/dwc3-keystone.c @@ -181,7 +181,7 @@ static int kdwc3_remove_core(struct device *dev, void *c) return 0; } -static int kdwc3_remove(struct platform_device *pdev) +static void kdwc3_remove(struct platform_device *pdev) { struct dwc3_keystone *kdwc = platform_get_drvdata(pdev); struct device_node *node = pdev->dev.of_node; @@ -198,8 +198,6 @@ static int kdwc3_remove(struct platform_device *pdev) phy_pm_runtime_put_sync(kdwc->usb3_phy); platform_set_drvdata(pdev, NULL); - - return 0; } static const struct of_device_id kdwc3_of_match[] = { @@ -211,7 +209,7 @@ MODULE_DEVICE_TABLE(of, kdwc3_of_match); static struct platform_driver kdwc3_driver = { .probe = kdwc3_probe, - .remove = kdwc3_remove, + .remove_new = kdwc3_remove, .driver = { .name = "keystone-dwc3", .of_match_table = kdwc3_of_match, -- cgit v1.2.3 From 3ffea6e0f34c767cbce35e3c541661d0ba18beed Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:19 +0200 Subject: usb: dwc3-meson-g12a: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Martin Blumenstingl Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20230517230239.187727-18-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-meson-g12a.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index a13afdb219e8..365aec00d302 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -826,7 +826,7 @@ err_disable_clks: return ret; } -static int dwc3_meson_g12a_remove(struct platform_device *pdev) +static void dwc3_meson_g12a_remove(struct platform_device *pdev) { struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; @@ -849,8 +849,6 @@ static int dwc3_meson_g12a_remove(struct platform_device *pdev) clk_bulk_disable_unprepare(priv->drvdata->num_clks, priv->drvdata->clks); - - return 0; } static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev) @@ -961,7 +959,7 @@ MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match); static struct platform_driver dwc3_meson_g12a_driver = { .probe = dwc3_meson_g12a_probe, - .remove = dwc3_meson_g12a_remove, + .remove_new = dwc3_meson_g12a_remove, .driver = { .name = "dwc3-meson-g12a", .of_match_table = dwc3_meson_g12a_match, -- cgit v1.2.3 From 86a2b452179becbeee1da60a37960326fe843f31 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:20 +0200 Subject: usb: dwc3-of-simple: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20230517230239.187727-19-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-of-simple.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-of-simple.c b/drivers/usb/dwc3/dwc3-of-simple.c index 71fd620c5161..7e6ad8fe61a5 100644 --- a/drivers/usb/dwc3/dwc3-of-simple.c +++ b/drivers/usb/dwc3/dwc3-of-simple.c @@ -112,13 +112,11 @@ static void __dwc3_of_simple_teardown(struct dwc3_of_simple *simple) pm_runtime_set_suspended(simple->dev); } -static int dwc3_of_simple_remove(struct platform_device *pdev) +static void dwc3_of_simple_remove(struct platform_device *pdev) { struct dwc3_of_simple *simple = platform_get_drvdata(pdev); __dwc3_of_simple_teardown(simple); - - return 0; } static void dwc3_of_simple_shutdown(struct platform_device *pdev) @@ -183,7 +181,7 @@ MODULE_DEVICE_TABLE(of, of_dwc3_simple_match); static struct platform_driver dwc3_of_simple_driver = { .probe = dwc3_of_simple_probe, - .remove = dwc3_of_simple_remove, + .remove_new = dwc3_of_simple_remove, .shutdown = dwc3_of_simple_shutdown, .driver = { .name = "dwc3-of-simple", -- cgit v1.2.3 From abe04efc13edf253a57c1f92b8e83c443a4d670a Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:21 +0200 Subject: usb: dwc3-omap: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20230517230239.187727-20-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-omap.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index efaf0db595f4..d5c77db4daa9 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -534,7 +534,7 @@ err1: return ret; } -static int dwc3_omap_remove(struct platform_device *pdev) +static void dwc3_omap_remove(struct platform_device *pdev) { struct dwc3_omap *omap = platform_get_drvdata(pdev); @@ -543,8 +543,6 @@ static int dwc3_omap_remove(struct platform_device *pdev) of_platform_depopulate(omap->dev); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - - return 0; } static const struct of_device_id of_dwc3_match[] = { @@ -611,7 +609,7 @@ static const struct dev_pm_ops dwc3_omap_dev_pm_ops = { static struct platform_driver dwc3_omap_driver = { .probe = dwc3_omap_probe, - .remove = dwc3_omap_remove, + .remove_new = dwc3_omap_remove, .driver = { .name = "omap-dwc3", .of_match_table = of_dwc3_match, -- cgit v1.2.3 From d662268dcca8c0ec72e0bf0b49738a3c0226a254 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:22 +0200 Subject: usb: dwc3-qcom: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Konrad Dybcio Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20230517230239.187727-21-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-qcom.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 959fc925ca7c..167f851c8e59 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -938,7 +938,7 @@ reset_assert: return ret; } -static int dwc3_qcom_remove(struct platform_device *pdev) +static void dwc3_qcom_remove(struct platform_device *pdev) { struct dwc3_qcom *qcom = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; @@ -958,8 +958,6 @@ static int dwc3_qcom_remove(struct platform_device *pdev) pm_runtime_allow(dev); pm_runtime_disable(dev); - - return 0; } static int __maybe_unused dwc3_qcom_pm_suspend(struct device *dev) @@ -1052,7 +1050,7 @@ MODULE_DEVICE_TABLE(acpi, dwc3_qcom_acpi_match); static struct platform_driver dwc3_qcom_driver = { .probe = dwc3_qcom_probe, - .remove = dwc3_qcom_remove, + .remove_new = dwc3_qcom_remove, .driver = { .name = "dwc3-qcom", .pm = &dwc3_qcom_dev_pm_ops, -- cgit v1.2.3 From 2f6453761e7c928367a58b49f451a2ddfc191ab2 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:23 +0200 Subject: usb: dwc3-st: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20230517230239.187727-22-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-st.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c index fea5290de83f..211360eee95a 100644 --- a/drivers/usb/dwc3/dwc3-st.c +++ b/drivers/usb/dwc3/dwc3-st.c @@ -305,7 +305,7 @@ undo_platform_dev_alloc: return ret; } -static int st_dwc3_remove(struct platform_device *pdev) +static void st_dwc3_remove(struct platform_device *pdev) { struct st_dwc3 *dwc3_data = platform_get_drvdata(pdev); @@ -313,8 +313,6 @@ static int st_dwc3_remove(struct platform_device *pdev) reset_control_assert(dwc3_data->rstc_pwrdn); reset_control_assert(dwc3_data->rstc_rst); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -364,7 +362,7 @@ MODULE_DEVICE_TABLE(of, st_dwc3_match); static struct platform_driver st_dwc3_driver = { .probe = st_dwc3_probe, - .remove = st_dwc3_remove, + .remove_new = st_dwc3_remove, .driver = { .name = "usb-st-dwc3", .of_match_table = st_dwc3_match, -- cgit v1.2.3 From 5b3eb973bf385b6e62368d2428e6b3cba3a0b260 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:24 +0200 Subject: usb: dwc3-xilinx: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Thinh Nguyen Link: https://lore.kernel.org/r/20230517230239.187727-23-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-xilinx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-xilinx.c b/drivers/usb/dwc3/dwc3-xilinx.c index 2c36f97652ca..19307d24f3a0 100644 --- a/drivers/usb/dwc3/dwc3-xilinx.c +++ b/drivers/usb/dwc3/dwc3-xilinx.c @@ -305,7 +305,7 @@ err_clk_put: return ret; } -static int dwc3_xlnx_remove(struct platform_device *pdev) +static void dwc3_xlnx_remove(struct platform_device *pdev) { struct dwc3_xlnx *priv_data = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; @@ -318,8 +318,6 @@ static int dwc3_xlnx_remove(struct platform_device *pdev) pm_runtime_disable(dev); pm_runtime_put_noidle(dev); pm_runtime_set_suspended(dev); - - return 0; } static int __maybe_unused dwc3_xlnx_runtime_suspend(struct device *dev) @@ -388,7 +386,7 @@ static const struct dev_pm_ops dwc3_xlnx_dev_pm_ops = { static struct platform_driver dwc3_xlnx_driver = { .probe = dwc3_xlnx_probe, - .remove = dwc3_xlnx_remove, + .remove_new = dwc3_xlnx_remove, .driver = { .name = "dwc3-xilinx", .of_match_table = dwc3_xlnx_of_match, -- cgit v1.2.3 From 2c16f04d24b164af99c86fa1912f48e6bb2e9125 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:25 +0200 Subject: usb: fotg210: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Linus Walleij Link: https://lore.kernel.org/r/20230517230239.187727-24-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/fotg210/fotg210-core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/fotg210/fotg210-core.c b/drivers/usb/fotg210/fotg210-core.c index cb75464ab290..958fc40eae86 100644 --- a/drivers/usb/fotg210/fotg210-core.c +++ b/drivers/usb/fotg210/fotg210-core.c @@ -165,7 +165,7 @@ static int fotg210_probe(struct platform_device *pdev) return ret; } -static int fotg210_remove(struct platform_device *pdev) +static void fotg210_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; enum usb_dr_mode mode; @@ -176,8 +176,6 @@ static int fotg210_remove(struct platform_device *pdev) fotg210_udc_remove(pdev); else fotg210_hcd_remove(pdev); - - return 0; } #ifdef CONFIG_OF @@ -196,7 +194,7 @@ static struct platform_driver fotg210_driver = { .of_match_table = of_match_ptr(fotg210_of_match), }, .probe = fotg210_probe, - .remove = fotg210_remove, + .remove_new = fotg210_remove, }; static int __init fotg210_init(void) -- cgit v1.2.3 From 530bf2c69cda96577c1a8d7213c702fd5f1064cc Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:26 +0200 Subject: usb: gadget: hid: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-25-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/legacy/hid.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/legacy/hid.c b/drivers/usb/gadget/legacy/hid.c index 133daf88162e..4ca67b2f8f24 100644 --- a/drivers/usb/gadget/legacy/hid.c +++ b/drivers/usb/gadget/legacy/hid.c @@ -237,7 +237,7 @@ static int hidg_plat_driver_probe(struct platform_device *pdev) return 0; } -static int hidg_plat_driver_remove(struct platform_device *pdev) +static void hidg_plat_driver_remove(struct platform_device *pdev) { struct hidg_func_node *e, *n; @@ -245,8 +245,6 @@ static int hidg_plat_driver_remove(struct platform_device *pdev) list_del(&e->node); kfree(e); } - - return 0; } @@ -263,7 +261,7 @@ static struct usb_composite_driver hidg_driver = { }; static struct platform_driver hidg_plat_driver = { - .remove = hidg_plat_driver_remove, + .remove_new = hidg_plat_driver_remove, .driver = { .name = "hidg", }, -- cgit v1.2.3 From ba170e197541702b56c1e5ff49c4379a3e59b1ae Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:27 +0200 Subject: usb: gadget: aspeed: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-26-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/aspeed-vhub/core.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c index 86398a04a012..16f2db8c4a2b 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/core.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c @@ -253,14 +253,14 @@ void ast_vhub_init_hw(struct ast_vhub *vhub) vhub->regs + AST_VHUB_IER); } -static int ast_vhub_remove(struct platform_device *pdev) +static void ast_vhub_remove(struct platform_device *pdev) { struct ast_vhub *vhub = platform_get_drvdata(pdev); unsigned long flags; int i; if (!vhub || !vhub->regs) - return 0; + return; /* Remove devices */ for (i = 0; i < vhub->max_ports; i++) @@ -289,8 +289,6 @@ static int ast_vhub_remove(struct platform_device *pdev) vhub->ep0_bufs, vhub->ep0_bufs_dma); vhub->ep0_bufs = NULL; - - return 0; } static int ast_vhub_probe(struct platform_device *pdev) @@ -431,7 +429,7 @@ MODULE_DEVICE_TABLE(of, ast_vhub_dt_ids); static struct platform_driver ast_vhub_driver = { .probe = ast_vhub_probe, - .remove = ast_vhub_remove, + .remove_new = ast_vhub_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = ast_vhub_dt_ids, -- cgit v1.2.3 From e28137b056ced4e014a2e2206b8d59236c40d369 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:28 +0200 Subject: usb: gadget/atmel_usba_udc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Claudiu Beznea Link: https://lore.kernel.org/r/20230517230239.187727-27-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/atmel_usba_udc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 53ca38c4b3ec..6c0ed3fa5eb1 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -2369,7 +2369,7 @@ static int usba_udc_probe(struct platform_device *pdev) return 0; } -static int usba_udc_remove(struct platform_device *pdev) +static void usba_udc_remove(struct platform_device *pdev) { struct usba_udc *udc; int i; @@ -2382,8 +2382,6 @@ static int usba_udc_remove(struct platform_device *pdev) for (i = 1; i < udc->num_ep; i++) usba_ep_cleanup_debugfs(&udc->usba_ep[i]); usba_cleanup_debugfs(udc); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -2450,7 +2448,7 @@ static SIMPLE_DEV_PM_OPS(usba_udc_pm_ops, usba_udc_suspend, usba_udc_resume); static struct platform_driver udc_driver = { .probe = usba_udc_probe, - .remove = usba_udc_remove, + .remove_new = usba_udc_remove, .driver = { .name = "atmel_usba_udc", .pm = &usba_udc_pm_ops, -- cgit v1.2.3 From 0621dacef2f451172f4a1b8fe19ee7a362edda65 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:29 +0200 Subject: usb: gadget/bcm63xx_udc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-28-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/bcm63xx_udc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c index a3055dd4acfb..da7011d906e0 100644 --- a/drivers/usb/gadget/udc/bcm63xx_udc.c +++ b/drivers/usb/gadget/udc/bcm63xx_udc.c @@ -2354,7 +2354,7 @@ report_request_failure: * bcm63xx_udc_remove - Remove the device from the system. * @pdev: Platform device struct from the bcm63xx BSP code. */ -static int bcm63xx_udc_remove(struct platform_device *pdev) +static void bcm63xx_udc_remove(struct platform_device *pdev) { struct bcm63xx_udc *udc = platform_get_drvdata(pdev); @@ -2363,13 +2363,11 @@ static int bcm63xx_udc_remove(struct platform_device *pdev) BUG_ON(udc->driver); bcm63xx_uninit_udc_hw(udc); - - return 0; } static struct platform_driver bcm63xx_udc_driver = { .probe = bcm63xx_udc_probe, - .remove = bcm63xx_udc_remove, + .remove_new = bcm63xx_udc_remove, .driver = { .name = DRV_MODULE_NAME, }, -- cgit v1.2.3 From ee8455c07c7b6330e446b77d4f026547c81c33a0 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:30 +0200 Subject: usb: bdc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20230517230239.187727-29-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/bdc/bdc_core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c index 9849e0c86e23..35a652807fca 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_core.c +++ b/drivers/usb/gadget/udc/bdc/bdc_core.c @@ -583,7 +583,7 @@ disable_clk: return ret; } -static int bdc_remove(struct platform_device *pdev) +static void bdc_remove(struct platform_device *pdev) { struct bdc *bdc; @@ -593,7 +593,6 @@ static int bdc_remove(struct platform_device *pdev) bdc_hw_exit(bdc); bdc_phy_exit(bdc); clk_disable_unprepare(bdc->clk); - return 0; } #ifdef CONFIG_PM_SLEEP @@ -648,7 +647,7 @@ static struct platform_driver bdc_driver = { .of_match_table = bdc_of_match, }, .probe = bdc_probe, - .remove = bdc_remove, + .remove_new = bdc_remove, }; module_platform_driver(bdc_driver); -- cgit v1.2.3 From 2dd3f64fcc11f77d7c66b3f47d0631f8f85d642a Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:31 +0200 Subject: usb: gadget/dummy_hcd: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-30-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/dummy_hcd.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 899ac9f9c279..0953e1b5c030 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -1108,13 +1108,12 @@ err_udc: return rc; } -static int dummy_udc_remove(struct platform_device *pdev) +static void dummy_udc_remove(struct platform_device *pdev) { struct dummy *dum = platform_get_drvdata(pdev); device_remove_file(&dum->gadget.dev, &dev_attr_function); usb_del_gadget_udc(&dum->gadget); - return 0; } static void dummy_udc_pm(struct dummy *dum, struct dummy_hcd *dum_hcd, @@ -1150,7 +1149,7 @@ static int dummy_udc_resume(struct platform_device *pdev) static struct platform_driver dummy_udc_driver = { .probe = dummy_udc_probe, - .remove = dummy_udc_remove, + .remove_new = dummy_udc_remove, .suspend = dummy_udc_suspend, .resume = dummy_udc_resume, .driver = { @@ -2701,7 +2700,7 @@ put_usb2_hcd: return retval; } -static int dummy_hcd_remove(struct platform_device *pdev) +static void dummy_hcd_remove(struct platform_device *pdev) { struct dummy *dum; @@ -2717,8 +2716,6 @@ static int dummy_hcd_remove(struct platform_device *pdev) dum->hs_hcd = NULL; dum->ss_hcd = NULL; - - return 0; } static int dummy_hcd_suspend(struct platform_device *pdev, pm_message_t state) @@ -2753,7 +2750,7 @@ static int dummy_hcd_resume(struct platform_device *pdev) static struct platform_driver dummy_hcd_driver = { .probe = dummy_hcd_probe, - .remove = dummy_hcd_remove, + .remove_new = dummy_hcd_remove, .suspend = dummy_hcd_suspend, .resume = dummy_hcd_resume, .driver = { -- cgit v1.2.3 From 43efe68158fc83b1b11f1562b6c66b16280c41f0 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:32 +0200 Subject: usb: gadget/fsl_qe_udc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-31-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/fsl_qe_udc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c index 3b1cc8fa30c8..9c5dc1c1a68e 100644 --- a/drivers/usb/gadget/udc/fsl_qe_udc.c +++ b/drivers/usb/gadget/udc/fsl_qe_udc.c @@ -2628,7 +2628,7 @@ static int qe_udc_resume(struct platform_device *dev) } #endif -static int qe_udc_remove(struct platform_device *ofdev) +static void qe_udc_remove(struct platform_device *ofdev) { struct qe_udc *udc = platform_get_drvdata(ofdev); struct qe_ep *ep; @@ -2679,8 +2679,6 @@ static int qe_udc_remove(struct platform_device *ofdev) /* wait for release() of gadget.dev to free udc */ wait_for_completion(&done); - - return 0; } /*-------------------------------------------------------------------------*/ @@ -2708,7 +2706,7 @@ static struct platform_driver udc_driver = { .of_match_table = qe_udc_match, }, .probe = qe_udc_probe, - .remove = qe_udc_remove, + .remove_new = qe_udc_remove, #ifdef CONFIG_PM .suspend = qe_udc_suspend, .resume = qe_udc_resume, -- cgit v1.2.3 From a864e8f27738a4e6429a13fe117f0d6ea7401c3a Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:33 +0200 Subject: usb: gadget/fusb300_udc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-32-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/fusb300_udc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c index 08ba9c8c1e67..bd03d475f927 100644 --- a/drivers/usb/gadget/udc/fusb300_udc.c +++ b/drivers/usb/gadget/udc/fusb300_udc.c @@ -1338,7 +1338,7 @@ static const struct usb_gadget_ops fusb300_gadget_ops = { .udc_stop = fusb300_udc_stop, }; -static int fusb300_remove(struct platform_device *pdev) +static void fusb300_remove(struct platform_device *pdev) { struct fusb300 *fusb300 = platform_get_drvdata(pdev); int i; @@ -1352,8 +1352,6 @@ static int fusb300_remove(struct platform_device *pdev) for (i = 0; i < FUSB300_MAX_NUM_EP; i++) kfree(fusb300->ep[i]); kfree(fusb300); - - return 0; } static int fusb300_probe(struct platform_device *pdev) @@ -1508,7 +1506,7 @@ clean_up: } static struct platform_driver fusb300_driver = { - .remove = fusb300_remove, + .remove_new = fusb300_remove, .driver = { .name = udc_name, }, -- cgit v1.2.3 From 9c78fc7bb731ac5aa2f333a135727a78cdda4bec Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:34 +0200 Subject: usb: gadget/m66592-udc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20230517230239.187727-33-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/m66592-udc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c index 06e21cee431b..e05f45a4b56b 100644 --- a/drivers/usb/gadget/udc/m66592-udc.c +++ b/drivers/usb/gadget/udc/m66592-udc.c @@ -1512,7 +1512,7 @@ static const struct usb_gadget_ops m66592_gadget_ops = { .pullup = m66592_pullup, }; -static int m66592_remove(struct platform_device *pdev) +static void m66592_remove(struct platform_device *pdev) { struct m66592 *m66592 = platform_get_drvdata(pdev); @@ -1527,7 +1527,6 @@ static int m66592_remove(struct platform_device *pdev) clk_put(m66592->clk); } kfree(m66592); - return 0; } static void nop_completion(struct usb_ep *ep, struct usb_request *r) @@ -1688,7 +1687,7 @@ clean_up: /*-------------------------------------------------------------------------*/ static struct platform_driver m66592_driver = { - .remove = m66592_remove, + .remove_new = m66592_remove, .driver = { .name = udc_name, }, -- cgit v1.2.3 From d5d4b4f2377a31a16171fbbee0c9b3710530a972 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:35 +0200 Subject: usb: gadget/mv_u3d_core: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-34-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/mv_u3d_core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c index 411b6179782c..3473048a85f5 100644 --- a/drivers/usb/gadget/udc/mv_u3d_core.c +++ b/drivers/usb/gadget/udc/mv_u3d_core.c @@ -1746,7 +1746,7 @@ static irqreturn_t mv_u3d_irq(int irq, void *dev) return IRQ_HANDLED; } -static int mv_u3d_remove(struct platform_device *dev) +static void mv_u3d_remove(struct platform_device *dev) { struct mv_u3d *u3d = platform_get_drvdata(dev); @@ -1775,8 +1775,6 @@ static int mv_u3d_remove(struct platform_device *dev) clk_put(u3d->clk); kfree(u3d); - - return 0; } static int mv_u3d_probe(struct platform_device *dev) @@ -2049,7 +2047,7 @@ static void mv_u3d_shutdown(struct platform_device *dev) static struct platform_driver mv_u3d_driver = { .probe = mv_u3d_probe, - .remove = mv_u3d_remove, + .remove_new = mv_u3d_remove, .shutdown = mv_u3d_shutdown, .driver = { .name = "mv-u3d", -- cgit v1.2.3 From 7a0bfca4b379df8410605b99601c5bc966afff18 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:36 +0200 Subject: usb: gadget/mv_udc_core: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-35-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/mv_udc_core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c index 08474c08d874..79db74e2040b 100644 --- a/drivers/usb/gadget/udc/mv_udc_core.c +++ b/drivers/usb/gadget/udc/mv_udc_core.c @@ -2077,7 +2077,7 @@ static void gadget_release(struct device *_dev) complete(udc->done); } -static int mv_udc_remove(struct platform_device *pdev) +static void mv_udc_remove(struct platform_device *pdev) { struct mv_udc *udc; @@ -2099,8 +2099,6 @@ static int mv_udc_remove(struct platform_device *pdev) /* free dev, wait for the release() finished */ wait_for_completion(udc->done); - - return 0; } static int mv_udc_probe(struct platform_device *pdev) @@ -2411,7 +2409,7 @@ static void mv_udc_shutdown(struct platform_device *pdev) static struct platform_driver udc_driver = { .probe = mv_udc_probe, - .remove = mv_udc_remove, + .remove_new = mv_udc_remove, .shutdown = mv_udc_shutdown, .driver = { .name = "mv-udc", -- cgit v1.2.3 From e4707226011cf376b8f7da5d66cec73e2a2300a6 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:37 +0200 Subject: usb: gadget/net2272: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-36-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/net2272.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index 538c1b9a2883..12e76bb62c20 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -2670,7 +2670,7 @@ net2272_plat_probe(struct platform_device *pdev) return ret; } -static int +static void net2272_plat_remove(struct platform_device *pdev) { struct net2272 *dev = platform_get_drvdata(pdev); @@ -2681,13 +2681,11 @@ net2272_plat_remove(struct platform_device *pdev) resource_size(&pdev->resource[0])); usb_put_gadget(&dev->gadget); - - return 0; } static struct platform_driver net2272_plat_driver = { .probe = net2272_plat_probe, - .remove = net2272_plat_remove, + .remove_new = net2272_plat_remove, .driver = { .name = driver_name, }, -- cgit v1.2.3 From 9225afafc1b8ccf31529a6064c8599be93bfbf2b Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:38 +0200 Subject: usb: gadget/omap_udc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-37-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/omap_udc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c index 2d87c7cd5f7e..10c5d7f726a1 100644 --- a/drivers/usb/gadget/udc/omap_udc.c +++ b/drivers/usb/gadget/udc/omap_udc.c @@ -2927,7 +2927,7 @@ cleanup0: return status; } -static int omap_udc_remove(struct platform_device *pdev) +static void omap_udc_remove(struct platform_device *pdev) { DECLARE_COMPLETION_ONSTACK(done); @@ -2939,8 +2939,6 @@ static int omap_udc_remove(struct platform_device *pdev) release_mem_region(pdev->resource[0].start, resource_size(&pdev->resource[0])); - - return 0; } /* suspend/resume/wakeup from sysfs (echo > power/state) or when the @@ -2985,7 +2983,7 @@ static int omap_udc_resume(struct platform_device *dev) static struct platform_driver udc_driver = { .probe = omap_udc_probe, - .remove = omap_udc_remove, + .remove_new = omap_udc_remove, .suspend = omap_udc_suspend, .resume = omap_udc_resume, .driver = { -- cgit v1.2.3 From 570d6d89783b0fb875524be6d279ba7c7c012f79 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:39 +0200 Subject: usb: gadget/pxa27x_udc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-38-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/pxa27x_udc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 0ecdfd2ba9e9..ab31fe35f059 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -2445,7 +2445,7 @@ err: * pxa_udc_remove - removes the udc device driver * @_dev: platform device */ -static int pxa_udc_remove(struct platform_device *_dev) +static void pxa_udc_remove(struct platform_device *_dev) { struct pxa_udc *udc = platform_get_drvdata(_dev); @@ -2460,8 +2460,6 @@ static int pxa_udc_remove(struct platform_device *_dev) udc->transceiver = NULL; the_controller = NULL; clk_unprepare(udc->clk); - - return 0; } static void pxa_udc_shutdown(struct platform_device *_dev) @@ -2547,7 +2545,7 @@ static struct platform_driver udc_driver = { .of_match_table = of_match_ptr(udc_pxa_dt_ids), }, .probe = pxa_udc_probe, - .remove = pxa_udc_remove, + .remove_new = pxa_udc_remove, .shutdown = pxa_udc_shutdown, #ifdef CONFIG_PM .suspend = pxa_udc_suspend, -- cgit v1.2.3 From 9da2fa8fb8d445561b7d84cd7238c8a000ae5d84 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:40 +0200 Subject: usb: gadget/r8a66597-udc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20230517230239.187727-39-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/r8a66597-udc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index 38e4d6b505a0..51b665f15c8e 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1805,7 +1805,7 @@ static const struct usb_gadget_ops r8a66597_gadget_ops = { .set_selfpowered = r8a66597_set_selfpowered, }; -static int r8a66597_remove(struct platform_device *pdev) +static void r8a66597_remove(struct platform_device *pdev) { struct r8a66597 *r8a66597 = platform_get_drvdata(pdev); @@ -1816,8 +1816,6 @@ static int r8a66597_remove(struct platform_device *pdev) if (r8a66597->pdata->on_chip) { clk_disable_unprepare(r8a66597->clk); } - - return 0; } static void nop_completion(struct usb_ep *ep, struct usb_request *r) @@ -1966,7 +1964,7 @@ clean_up2: /*-------------------------------------------------------------------------*/ static struct platform_driver r8a66597_driver = { - .remove = r8a66597_remove, + .remove_new = r8a66597_remove, .driver = { .name = udc_name, }, -- cgit v1.2.3 From 3ffd57792621fee59d271934d66c158b6c45b84a Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:41 +0200 Subject: usb: gadget/renesas_usb3: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Geert Uytterhoeven Reviewed-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20230517230239.187727-40-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/renesas_usb3.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/renesas_usb3.c b/drivers/usb/gadget/udc/renesas_usb3.c index aac8bc185afa..7443b60da5d2 100644 --- a/drivers/usb/gadget/udc/renesas_usb3.c +++ b/drivers/usb/gadget/udc/renesas_usb3.c @@ -2653,7 +2653,7 @@ static void renesas_usb3_debugfs_init(struct renesas_usb3 *usb3, } /*------- platform_driver ------------------------------------------------*/ -static int renesas_usb3_remove(struct platform_device *pdev) +static void renesas_usb3_remove(struct platform_device *pdev) { struct renesas_usb3 *usb3 = platform_get_drvdata(pdev); @@ -2669,8 +2669,6 @@ static int renesas_usb3_remove(struct platform_device *pdev) __renesas_usb3_ep_free_request(usb3->ep0_req); pm_runtime_disable(&pdev->dev); - - return 0; } static int renesas_usb3_init_ep(struct renesas_usb3 *usb3, struct device *dev, @@ -3015,7 +3013,7 @@ static SIMPLE_DEV_PM_OPS(renesas_usb3_pm_ops, renesas_usb3_suspend, static struct platform_driver renesas_usb3_driver = { .probe = renesas_usb3_probe, - .remove = renesas_usb3_remove, + .remove_new = renesas_usb3_remove, .driver = { .name = udc_name, .pm = &renesas_usb3_pm_ops, -- cgit v1.2.3 From cc3ee267e4c56c1cb09709f923128b32e4e19015 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:42 +0200 Subject: usb: gadget/renesas_usbf: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Herve Codina Reviewed-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20230517230239.187727-41-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/renesas_usbf.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/renesas_usbf.c b/drivers/usb/gadget/udc/renesas_usbf.c index 84ac9fe4ce7f..6cd0af83e91e 100644 --- a/drivers/usb/gadget/udc/renesas_usbf.c +++ b/drivers/usb/gadget/udc/renesas_usbf.c @@ -3361,15 +3361,13 @@ static int usbf_probe(struct platform_device *pdev) return 0; } -static int usbf_remove(struct platform_device *pdev) +static void usbf_remove(struct platform_device *pdev) { struct usbf_udc *udc = platform_get_drvdata(pdev); usb_del_gadget_udc(&udc->gadget); pm_runtime_put(&pdev->dev); - - return 0; } static const struct of_device_id usbf_match[] = { @@ -3385,7 +3383,7 @@ static struct platform_driver udc_driver = { .of_match_table = usbf_match, }, .probe = usbf_probe, - .remove = usbf_remove, + .remove_new = usbf_remove, }; module_platform_driver(udc_driver); -- cgit v1.2.3 From 98f2a546ac5c2c8b821ac2b9e2cd9e9102e77a54 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:43 +0200 Subject: usb: gadget/rzv2m_usb3drd: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Biju Das Link: https://lore.kernel.org/r/20230517230239.187727-42-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/rzv2m_usb3drd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/rzv2m_usb3drd.c b/drivers/usb/gadget/udc/rzv2m_usb3drd.c index 589c7252e014..36f4ff00d22f 100644 --- a/drivers/usb/gadget/udc/rzv2m_usb3drd.c +++ b/drivers/usb/gadget/udc/rzv2m_usb3drd.c @@ -58,7 +58,7 @@ void rzv2m_usb3drd_reset(struct device *dev, bool host) } EXPORT_SYMBOL_GPL(rzv2m_usb3drd_reset); -static int rzv2m_usb3drd_remove(struct platform_device *pdev) +static void rzv2m_usb3drd_remove(struct platform_device *pdev) { struct rzv2m_usb3drd *usb3 = platform_get_drvdata(pdev); @@ -66,8 +66,6 @@ static int rzv2m_usb3drd_remove(struct platform_device *pdev) pm_runtime_put(usb3->dev); pm_runtime_disable(&pdev->dev); reset_control_assert(usb3->drd_rstc); - - return 0; } static int rzv2m_usb3drd_probe(struct platform_device *pdev) @@ -129,7 +127,7 @@ static struct platform_driver rzv2m_usb3drd_driver = { .of_match_table = rzv2m_usb3drd_of_match, }, .probe = rzv2m_usb3drd_probe, - .remove = rzv2m_usb3drd_remove, + .remove_new = rzv2m_usb3drd_remove, }; module_platform_driver(rzv2m_usb3drd_driver); -- cgit v1.2.3 From dad23c87a48e844dca18445b7e1677e4ce25d6fc Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:44 +0200 Subject: usb: gadget/snps_udc_plat: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-43-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/snps_udc_plat.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/udc/snps_udc_plat.c b/drivers/usb/gadget/udc/snps_udc_plat.c index 0d3e705655b9..0ed685db149d 100644 --- a/drivers/usb/gadget/udc/snps_udc_plat.c +++ b/drivers/usb/gadget/udc/snps_udc_plat.c @@ -225,7 +225,7 @@ exit_phy: return ret; } -static int udc_plat_remove(struct platform_device *pdev) +static void udc_plat_remove(struct platform_device *pdev) { struct udc *dev; @@ -234,7 +234,7 @@ static int udc_plat_remove(struct platform_device *pdev) usb_del_gadget_udc(&dev->gadget); /* gadget driver must not be registered */ if (WARN_ON(dev->driver)) - return 0; + return; /* dma pool cleanup */ free_dma_pools(dev); @@ -248,8 +248,6 @@ static int udc_plat_remove(struct platform_device *pdev) extcon_unregister_notifier(dev->edev, EXTCON_USB, &dev->nb); dev_info(&pdev->dev, "Synopsys UDC platform driver removed\n"); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -315,7 +313,7 @@ MODULE_DEVICE_TABLE(of, of_udc_match); static struct platform_driver udc_plat_driver = { .probe = udc_plat_probe, - .remove = udc_plat_remove, + .remove_new = udc_plat_remove, .driver = { .name = "snps-udc-plat", .of_match_table = of_match_ptr(of_udc_match), -- cgit v1.2.3 From 48c125b55aa29ba55b9331add94f55b5d7b4c2dc Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:45 +0200 Subject: usb: gadget/tegra-xudc: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-44-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/tegra-xudc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index 34e9c1df54c7..83eaa65ddde3 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -3906,7 +3906,7 @@ put_padctl: return err; } -static int tegra_xudc_remove(struct platform_device *pdev) +static void tegra_xudc_remove(struct platform_device *pdev) { struct tegra_xudc *xudc = platform_get_drvdata(pdev); unsigned int i; @@ -3936,8 +3936,6 @@ static int tegra_xudc_remove(struct platform_device *pdev) pm_runtime_put(xudc->dev); tegra_xusb_padctl_put(xudc->padctl); - - return 0; } static int __maybe_unused tegra_xudc_powergate(struct tegra_xudc *xudc) @@ -4063,7 +4061,7 @@ static const struct dev_pm_ops tegra_xudc_pm_ops = { static struct platform_driver tegra_xudc_driver = { .probe = tegra_xudc_probe, - .remove = tegra_xudc_remove, + .remove_new = tegra_xudc_remove, .driver = { .name = "tegra-xudc", .pm = &tegra_xudc_pm_ops, -- cgit v1.2.3 From cffdf49d739b3dc555c742dcf6cceb52e9a9a0df Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:46 +0200 Subject: usb: gadget/udc-xilinx: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-45-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/udc-xilinx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index 4827e3cd3834..d73111b5cd84 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -2178,14 +2178,12 @@ fail: * * Return: 0 always */ -static int xudc_remove(struct platform_device *pdev) +static void xudc_remove(struct platform_device *pdev) { struct xusb_udc *udc = platform_get_drvdata(pdev); usb_del_gadget_udc(&udc->gadget); clk_disable_unprepare(udc->clk); - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -2257,7 +2255,7 @@ static struct platform_driver xudc_driver = { .pm = &xudc_pm_ops, }, .probe = xudc_probe, - .remove = xudc_remove, + .remove_new = xudc_remove, }; module_platform_driver(xudc_driver); -- cgit v1.2.3 From 5cad5686f2fbc36892425ad96e9c8ef1c8b66186 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:47 +0200 Subject: usb: ehci-atmel: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Claudiu Beznea Link: https://lore.kernel.org/r/20230517230239.187727-46-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-atmel.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index 8b775e7bab06..61808c51e702 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -173,7 +173,7 @@ fail_create_hcd: return retval; } -static int ehci_atmel_drv_remove(struct platform_device *pdev) +static void ehci_atmel_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); @@ -181,8 +181,6 @@ static int ehci_atmel_drv_remove(struct platform_device *pdev) usb_put_hcd(hcd); atmel_stop_ehci(pdev); - - return 0; } static int __maybe_unused ehci_atmel_drv_suspend(struct device *dev) @@ -223,7 +221,7 @@ static SIMPLE_DEV_PM_OPS(ehci_atmel_pm_ops, ehci_atmel_drv_suspend, static struct platform_driver ehci_atmel_driver = { .probe = ehci_atmel_drv_probe, - .remove = ehci_atmel_drv_remove, + .remove_new = ehci_atmel_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "atmel-ehci", -- cgit v1.2.3 From 8ea6a6ab52295d9e8f8383f4deab7ec8f6e6f6b7 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:48 +0200 Subject: usb: ehci-brcm: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20230517230239.187727-47-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-brcm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/ehci-brcm.c b/drivers/usb/host/ehci-brcm.c index 6a0f64c9e5e8..0362a082abb4 100644 --- a/drivers/usb/host/ehci-brcm.c +++ b/drivers/usb/host/ehci-brcm.c @@ -188,7 +188,7 @@ err_hcd: return err; } -static int ehci_brcm_remove(struct platform_device *dev) +static void ehci_brcm_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct brcm_priv *priv = hcd_to_ehci_priv(hcd); @@ -196,7 +196,6 @@ static int ehci_brcm_remove(struct platform_device *dev) usb_remove_hcd(hcd); clk_disable_unprepare(priv->clk); usb_put_hcd(hcd); - return 0; } static int __maybe_unused ehci_brcm_suspend(struct device *dev) @@ -250,7 +249,7 @@ static const struct of_device_id brcm_ehci_of_match[] = { static struct platform_driver ehci_brcm_driver = { .probe = ehci_brcm_probe, - .remove = ehci_brcm_remove, + .remove_new = ehci_brcm_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ehci-brcm", -- cgit v1.2.3 From 1043c6ba7d746b630d33d876244345335243f781 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:49 +0200 Subject: usb: ehci-exynos: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-48-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-exynos.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ehci-exynos.c b/drivers/usb/host/ehci-exynos.c index 47c9f06c3d84..20f8c0ec6810 100644 --- a/drivers/usb/host/ehci-exynos.c +++ b/drivers/usb/host/ehci-exynos.c @@ -230,7 +230,7 @@ fail_clk: return err; } -static int exynos_ehci_remove(struct platform_device *pdev) +static void exynos_ehci_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd); @@ -244,8 +244,6 @@ static int exynos_ehci_remove(struct platform_device *pdev) clk_disable_unprepare(exynos_ehci->clk); usb_put_hcd(hcd); - - return 0; } #ifdef CONFIG_PM @@ -311,7 +309,7 @@ MODULE_DEVICE_TABLE(of, exynos_ehci_match); static struct platform_driver exynos_ehci_driver = { .probe = exynos_ehci_probe, - .remove = exynos_ehci_remove, + .remove_new = exynos_ehci_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "exynos-ehci", -- cgit v1.2.3 From 453fb0aaee9b5799ac6758fb0fe3ca91cc0d0148 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:50 +0200 Subject: usb: ehci-fsl: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-49-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-fsl.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index d74fa5ba845b..81d60a695510 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -684,7 +684,7 @@ static const struct ehci_driver_overrides ehci_fsl_overrides __initconst = { * * Reverses the effect of usb_hcd_fsl_probe(). */ -static int fsl_ehci_drv_remove(struct platform_device *pdev) +static void fsl_ehci_drv_remove(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); struct usb_hcd *hcd = platform_get_drvdata(pdev); @@ -703,13 +703,11 @@ static int fsl_ehci_drv_remove(struct platform_device *pdev) if (pdata->exit) pdata->exit(pdev); usb_put_hcd(hcd); - - return 0; } static struct platform_driver ehci_fsl_driver = { .probe = fsl_ehci_drv_probe, - .remove = fsl_ehci_drv_remove, + .remove_new = fsl_ehci_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = DRV_NAME, -- cgit v1.2.3 From 3861844766ab16ed23403cecab787e1ab2cd4cc4 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:51 +0200 Subject: usb: ehci-grlib: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-50-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-grlib.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c index 0717f2ccf49d..14150e4d3382 100644 --- a/drivers/usb/host/ehci-grlib.c +++ b/drivers/usb/host/ehci-grlib.c @@ -140,7 +140,7 @@ err_irq: } -static int ehci_hcd_grlib_remove(struct platform_device *op) +static void ehci_hcd_grlib_remove(struct platform_device *op) { struct usb_hcd *hcd = platform_get_drvdata(op); @@ -151,8 +151,6 @@ static int ehci_hcd_grlib_remove(struct platform_device *op) irq_dispose_mapping(hcd->irq); usb_put_hcd(hcd); - - return 0; } @@ -170,7 +168,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_grlib_of_match); static struct platform_driver ehci_grlib_driver = { .probe = ehci_hcd_grlib_probe, - .remove = ehci_hcd_grlib_remove, + .remove_new = ehci_hcd_grlib_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "grlib-ehci", -- cgit v1.2.3 From 450955d77ae35e0bc11d6fa997376765520acc61 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:52 +0200 Subject: usb: ehci-mv: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-51-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-mv.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c index fa46d217dd10..9320cf0e5bc7 100644 --- a/drivers/usb/host/ehci-mv.c +++ b/drivers/usb/host/ehci-mv.c @@ -235,7 +235,7 @@ err_put_hcd: return retval; } -static int mv_ehci_remove(struct platform_device *pdev) +static void mv_ehci_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct ehci_hcd_mv *ehci_mv = hcd_to_ehci_hcd_mv(hcd); @@ -254,8 +254,6 @@ static int mv_ehci_remove(struct platform_device *pdev) } usb_put_hcd(hcd); - - return 0; } static const struct platform_device_id ehci_id_table[] = { @@ -282,7 +280,7 @@ static const struct of_device_id ehci_mv_dt_ids[] = { static struct platform_driver ehci_mv_driver = { .probe = mv_ehci_probe, - .remove = mv_ehci_remove, + .remove_new = mv_ehci_remove, .shutdown = mv_ehci_shutdown, .driver = { .name = "mv-ehci", -- cgit v1.2.3 From 7d6d8199575d3d5b288ac127384736f4f8bd3968 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:53 +0200 Subject: usb: ehci-npcm7xx: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-52-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-npcm7xx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ehci-npcm7xx.c b/drivers/usb/host/ehci-npcm7xx.c index 63af1a827fcb..ad79ce63afcf 100644 --- a/drivers/usb/host/ehci-npcm7xx.c +++ b/drivers/usb/host/ehci-npcm7xx.c @@ -106,15 +106,13 @@ fail: return retval; } -static int npcm7xx_ehci_hcd_drv_remove(struct platform_device *pdev) +static void npcm7xx_ehci_hcd_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); usb_remove_hcd(hcd); usb_put_hcd(hcd); - - return 0; } static const struct of_device_id npcm7xx_ehci_id_table[] = { @@ -125,7 +123,7 @@ MODULE_DEVICE_TABLE(of, npcm7xx_ehci_id_table); static struct platform_driver npcm7xx_ehci_hcd_driver = { .probe = npcm7xx_ehci_hcd_drv_probe, - .remove = npcm7xx_ehci_hcd_drv_remove, + .remove_new = npcm7xx_ehci_hcd_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "npcm7xx-ehci", -- cgit v1.2.3 From 1bd418b6aafd086228d6cfb238db494fdcb88cfd Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:54 +0200 Subject: usb: ehci-omap: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-53-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-omap.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 7dd984722a7f..cb6509a735ac 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -237,7 +237,7 @@ err_phy: * the HCD's stop() method. It is always called from a thread * context, normally "rmmod", "apmd", or something similar. */ -static int ehci_hcd_omap_remove(struct platform_device *pdev) +static void ehci_hcd_omap_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct usb_hcd *hcd = dev_get_drvdata(dev); @@ -254,8 +254,6 @@ static int ehci_hcd_omap_remove(struct platform_device *pdev) usb_put_hcd(hcd); pm_runtime_put_sync(dev); pm_runtime_disable(dev); - - return 0; } static const struct of_device_id omap_ehci_dt_ids[] = { @@ -267,7 +265,7 @@ MODULE_DEVICE_TABLE(of, omap_ehci_dt_ids); static struct platform_driver ehci_hcd_omap_driver = { .probe = ehci_hcd_omap_probe, - .remove = ehci_hcd_omap_remove, + .remove_new = ehci_hcd_omap_remove, .shutdown = usb_hcd_platform_shutdown, /*.suspend = ehci_hcd_omap_suspend, */ /*.resume = ehci_hcd_omap_resume, */ -- cgit v1.2.3 From c554264609c257acadf0d77dcf9232202769acff Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:55 +0200 Subject: usb: ehci-orion: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-54-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-orion.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index a3454a3ea4e0..2cfb27dc943a 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -321,7 +321,7 @@ err: return err; } -static int ehci_orion_drv_remove(struct platform_device *pdev) +static void ehci_orion_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct orion_ehci_hcd *priv = hcd_to_orion_priv(hcd); @@ -332,8 +332,6 @@ static int ehci_orion_drv_remove(struct platform_device *pdev) clk_disable_unprepare(priv->clk); usb_put_hcd(hcd); - - return 0; } static const struct of_device_id ehci_orion_dt_ids[] = { @@ -345,7 +343,7 @@ MODULE_DEVICE_TABLE(of, ehci_orion_dt_ids); static struct platform_driver ehci_orion_driver = { .probe = ehci_orion_drv_probe, - .remove = ehci_orion_drv_remove, + .remove_new = ehci_orion_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "orion-ehci", -- cgit v1.2.3 From b700b067f69e12f0a78c1de6d622b69b29ed6660 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:56 +0200 Subject: usb: ehci-platform: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20230517230239.187727-55-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-platform.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index fe497c876d76..83bf56c9424f 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -400,7 +400,7 @@ err_put_clks: return err; } -static int ehci_platform_remove(struct platform_device *dev) +static void ehci_platform_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); @@ -424,8 +424,6 @@ static int ehci_platform_remove(struct platform_device *dev) if (pdata == &ehci_platform_defaults) dev->dev.platform_data = NULL; - - return 0; } static int __maybe_unused ehci_platform_suspend(struct device *dev) @@ -511,7 +509,7 @@ static SIMPLE_DEV_PM_OPS(ehci_platform_pm_ops, ehci_platform_suspend, static struct platform_driver ehci_platform_driver = { .id_table = ehci_platform_table, .probe = ehci_platform_probe, - .remove = ehci_platform_remove, + .remove_new = ehci_platform_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ehci-platform", -- cgit v1.2.3 From 095486c46305c15c9a1252753dd9e596e86da0d6 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:57 +0200 Subject: usb: ehci-ppc-of: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-56-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-ppc-of.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index b3aa464e9d2c..7fd83e806ae4 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -184,7 +184,7 @@ err_irq: } -static int ehci_hcd_ppc_of_remove(struct platform_device *op) +static void ehci_hcd_ppc_of_remove(struct platform_device *op) { struct usb_hcd *hcd = platform_get_drvdata(op); struct ehci_hcd *ehci = hcd_to_ehci(hcd); @@ -216,8 +216,6 @@ static int ehci_hcd_ppc_of_remove(struct platform_device *op) } } usb_put_hcd(hcd); - - return 0; } @@ -232,7 +230,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_ppc_of_match); static struct platform_driver ehci_hcd_ppc_of_driver = { .probe = ehci_hcd_ppc_of_probe, - .remove = ehci_hcd_ppc_of_remove, + .remove_new = ehci_hcd_ppc_of_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ppc-of-ehci", -- cgit v1.2.3 From a9a49024640c2547efc646e6794f2eb14243a4f4 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:58 +0200 Subject: usb: ehci-sh: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-57-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-sh.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index c25c51d26f26..0520e762801d 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -147,7 +147,7 @@ fail_create_hcd: return ret; } -static int ehci_hcd_sh_remove(struct platform_device *pdev) +static void ehci_hcd_sh_remove(struct platform_device *pdev) { struct ehci_sh_priv *priv = platform_get_drvdata(pdev); struct usb_hcd *hcd = priv->hcd; @@ -157,8 +157,6 @@ static int ehci_hcd_sh_remove(struct platform_device *pdev) clk_disable(priv->fclk); clk_disable(priv->iclk); - - return 0; } static void ehci_hcd_sh_shutdown(struct platform_device *pdev) @@ -172,7 +170,7 @@ static void ehci_hcd_sh_shutdown(struct platform_device *pdev) static struct platform_driver ehci_hcd_sh_driver = { .probe = ehci_hcd_sh_probe, - .remove = ehci_hcd_sh_remove, + .remove_new = ehci_hcd_sh_remove, .shutdown = ehci_hcd_sh_shutdown, .driver = { .name = "sh_ehci", -- cgit v1.2.3 From b87578faa6c3afb22506476bb093c85ce341e120 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:01:59 +0200 Subject: usb: ehci-spear: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-58-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-spear.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index c4ddd1022f60..1407703649be 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -124,7 +124,7 @@ fail: return retval ; } -static int spear_ehci_hcd_drv_remove(struct platform_device *pdev) +static void spear_ehci_hcd_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct spear_ehci *sehci = to_spear_ehci(hcd); @@ -134,8 +134,6 @@ static int spear_ehci_hcd_drv_remove(struct platform_device *pdev) if (sehci->clk) clk_disable_unprepare(sehci->clk); usb_put_hcd(hcd); - - return 0; } static const struct of_device_id spear_ehci_id_table[] = { @@ -146,7 +144,7 @@ MODULE_DEVICE_TABLE(of, spear_ehci_id_table); static struct platform_driver spear_ehci_hcd_driver = { .probe = spear_ehci_hcd_drv_probe, - .remove = spear_ehci_hcd_drv_remove, + .remove_new = spear_ehci_hcd_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "spear-ehci", -- cgit v1.2.3 From 756caf5d11fe699cbdd0b5a3c5253bb0d38b5b7b Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:00 +0200 Subject: usb: ehci-st: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Patrice Chotard Link: https://lore.kernel.org/r/20230517230239.187727-59-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-st.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ehci-st.c b/drivers/usb/host/ehci-st.c index f731dc98c533..ee0976b815b4 100644 --- a/drivers/usb/host/ehci-st.c +++ b/drivers/usb/host/ehci-st.c @@ -252,7 +252,7 @@ err_put_hcd: return err; } -static int st_ehci_platform_remove(struct platform_device *dev) +static void st_ehci_platform_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); @@ -271,8 +271,6 @@ static int st_ehci_platform_remove(struct platform_device *dev) if (pdata == &ehci_platform_defaults) dev->dev.platform_data = NULL; - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -328,7 +326,7 @@ MODULE_DEVICE_TABLE(of, st_ehci_ids); static struct platform_driver ehci_platform_driver = { .probe = st_ehci_platform_probe, - .remove = st_ehci_platform_remove, + .remove_new = st_ehci_platform_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "st-ehci", -- cgit v1.2.3 From a30125d975f2c1fa94b0bdd5baea830b5c4fda52 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:01 +0200 Subject: usb: ehci-xilinx-of: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-60-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-xilinx-of.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index 3d7893747835..a2112c28f631 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -201,7 +201,7 @@ err_irq: * * Return: Always return 0 */ -static int ehci_hcd_xilinx_of_remove(struct platform_device *op) +static void ehci_hcd_xilinx_of_remove(struct platform_device *op) { struct usb_hcd *hcd = platform_get_drvdata(op); @@ -210,8 +210,6 @@ static int ehci_hcd_xilinx_of_remove(struct platform_device *op) usb_remove_hcd(hcd); usb_put_hcd(hcd); - - return 0; } static const struct of_device_id ehci_hcd_xilinx_of_match[] = { @@ -222,7 +220,7 @@ MODULE_DEVICE_TABLE(of, ehci_hcd_xilinx_of_match); static struct platform_driver ehci_hcd_xilinx_of_driver = { .probe = ehci_hcd_xilinx_of_probe, - .remove = ehci_hcd_xilinx_of_remove, + .remove_new = ehci_hcd_xilinx_of_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "xilinx-of-ehci", -- cgit v1.2.3 From 29ac274b996e6c55092e24df2ee703c412d91e58 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:02 +0200 Subject: usb: fsl-mph-dr-of: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-61-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/fsl-mph-dr-of.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index 9db909d12354..a9877f2569f4 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -265,10 +265,9 @@ static int __unregister_subdev(struct device *dev, void *d) return 0; } -static int fsl_usb2_mph_dr_of_remove(struct platform_device *ofdev) +static void fsl_usb2_mph_dr_of_remove(struct platform_device *ofdev) { device_for_each_child(&ofdev->dev, NULL, __unregister_subdev); - return 0; } #ifdef CONFIG_PPC_MPC512x @@ -362,7 +361,7 @@ static struct platform_driver fsl_usb2_mph_dr_driver = { .of_match_table = fsl_usb2_mph_dr_of_match, }, .probe = fsl_usb2_mph_dr_of_probe, - .remove = fsl_usb2_mph_dr_of_remove, + .remove_new = fsl_usb2_mph_dr_of_remove, }; module_platform_driver(fsl_usb2_mph_dr_driver); -- cgit v1.2.3 From 00b92772800b6bfb3f5198c0975aa3b33c657250 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:03 +0200 Subject: usb: isp116x-hcd: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-62-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp116x-hcd.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 49ae01487af4..a82d8926e922 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1526,14 +1526,14 @@ static const struct hc_driver isp116x_hc_driver = { /*----------------------------------------------------------------*/ -static int isp116x_remove(struct platform_device *pdev) +static void isp116x_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct isp116x *isp116x; struct resource *res; if (!hcd) - return 0; + return; isp116x = hcd_to_isp116x(hcd); remove_debug_file(isp116x); usb_remove_hcd(hcd); @@ -1548,7 +1548,6 @@ static int isp116x_remove(struct platform_device *pdev) release_mem_region(res->start, 2); usb_put_hcd(hcd); - return 0; } static int isp116x_probe(struct platform_device *pdev) @@ -1685,7 +1684,7 @@ MODULE_ALIAS("platform:isp116x-hcd"); static struct platform_driver isp116x_driver = { .probe = isp116x_probe, - .remove = isp116x_remove, + .remove_new = isp116x_remove, .suspend = isp116x_suspend, .resume = isp116x_resume, .driver = { -- cgit v1.2.3 From 66426dbb1a0de6046f5ab271c653d5ae8a1b3713 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:04 +0200 Subject: usb: isp1362-hcd: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-63-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/isp1362-hcd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index b0da143ef4be..606f0a64f3b7 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -2606,7 +2606,7 @@ static const struct hc_driver isp1362_hc_driver = { /*-------------------------------------------------------------------------*/ -static int isp1362_remove(struct platform_device *pdev) +static void isp1362_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); @@ -2617,8 +2617,6 @@ static int isp1362_remove(struct platform_device *pdev) DBG(0, "%s: put_hcd\n", __func__); usb_put_hcd(hcd); DBG(0, "%s: Done\n", __func__); - - return 0; } static int isp1362_probe(struct platform_device *pdev) @@ -2760,7 +2758,7 @@ static int isp1362_resume(struct platform_device *pdev) static struct platform_driver isp1362_driver = { .probe = isp1362_probe, - .remove = isp1362_remove, + .remove_new = isp1362_remove, .suspend = isp1362_suspend, .resume = isp1362_resume, -- cgit v1.2.3 From 9e60ab3ee6094da6dc08ac46a6c27139be572d72 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:05 +0200 Subject: usb: octeon-hcd: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-64-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/octeon-hcd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/octeon-hcd.c b/drivers/usb/host/octeon-hcd.c index a1cd81d4a114..19d5777f5db2 100644 --- a/drivers/usb/host/octeon-hcd.c +++ b/drivers/usb/host/octeon-hcd.c @@ -3680,7 +3680,7 @@ static int octeon_usb_probe(struct platform_device *pdev) return 0; } -static int octeon_usb_remove(struct platform_device *pdev) +static void octeon_usb_remove(struct platform_device *pdev) { int status; struct device *dev = &pdev->dev; @@ -3696,8 +3696,6 @@ static int octeon_usb_remove(struct platform_device *pdev) dev_dbg(dev, "USB shutdown failed with %d\n", status); usb_put_hcd(hcd); - - return 0; } static const struct of_device_id octeon_usb_match[] = { @@ -3714,7 +3712,7 @@ static struct platform_driver octeon_usb_driver = { .of_match_table = octeon_usb_match, }, .probe = octeon_usb_probe, - .remove = octeon_usb_remove, + .remove_new = octeon_usb_remove, }; static int __init octeon_usb_driver_init(void) -- cgit v1.2.3 From a87a68c70cd283631c3fb73393f33171ad17c61f Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:06 +0200 Subject: usb: ohci-at91: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Claudiu Beznea Link: https://lore.kernel.org/r/20230517230239.187727-65-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-at91.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 533537ef3c21..b9ce8d80f20b 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -599,7 +599,7 @@ static int ohci_hcd_at91_drv_probe(struct platform_device *pdev) return usb_hcd_at91_probe(&ohci_at91_hc_driver, pdev); } -static int ohci_hcd_at91_drv_remove(struct platform_device *pdev) +static void ohci_hcd_at91_drv_remove(struct platform_device *pdev) { struct at91_usbh_data *pdata = dev_get_platdata(&pdev->dev); int i; @@ -611,7 +611,6 @@ static int ohci_hcd_at91_drv_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); usb_hcd_at91_remove(platform_get_drvdata(pdev), pdev); - return 0; } static int __maybe_unused @@ -683,7 +682,7 @@ static SIMPLE_DEV_PM_OPS(ohci_hcd_at91_pm_ops, ohci_hcd_at91_drv_suspend, static struct platform_driver ohci_hcd_at91_driver = { .probe = ohci_hcd_at91_drv_probe, - .remove = ohci_hcd_at91_drv_remove, + .remove_new = ohci_hcd_at91_drv_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "at91_ohci", -- cgit v1.2.3 From 9053f4b186341a6443bfbf74e5accaf100d974f7 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:07 +0200 Subject: usb: ohci-da8xx: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-66-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-da8xx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index d4818e8d652b..e4191a868944 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -469,14 +469,12 @@ err: return error; } -static int ohci_da8xx_remove(struct platform_device *pdev) +static void ohci_da8xx_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); usb_remove_hcd(hcd); usb_put_hcd(hcd); - - return 0; } #ifdef CONFIG_PM @@ -533,7 +531,7 @@ static const struct ohci_driver_overrides da8xx_overrides __initconst = { */ static struct platform_driver ohci_hcd_da8xx_driver = { .probe = ohci_da8xx_probe, - .remove = ohci_da8xx_remove, + .remove_new = ohci_da8xx_remove, .shutdown = usb_hcd_platform_shutdown, #ifdef CONFIG_PM .suspend = ohci_da8xx_suspend, -- cgit v1.2.3 From 16fe06cdfdace6df20229f9fe8b31d7e4851c5c8 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:08 +0200 Subject: usb: ohci-exynos: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-67-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-exynos.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index 8af17c1ee5cc..ab31c459b32d 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -198,7 +198,7 @@ fail_clk: return err; } -static int exynos_ohci_remove(struct platform_device *pdev) +static void exynos_ohci_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd); @@ -212,8 +212,6 @@ static int exynos_ohci_remove(struct platform_device *pdev) clk_disable_unprepare(exynos_ohci->clk); usb_put_hcd(hcd); - - return 0; } static void exynos_ohci_shutdown(struct platform_device *pdev) @@ -285,7 +283,7 @@ MODULE_DEVICE_TABLE(of, exynos_ohci_match); static struct platform_driver exynos_ohci_driver = { .probe = exynos_ohci_probe, - .remove = exynos_ohci_remove, + .remove_new = exynos_ohci_remove, .shutdown = exynos_ohci_shutdown, .driver = { .name = "exynos-ohci", -- cgit v1.2.3 From 70a52ca2ef1f99ed0dd69e3df562bf3480a76141 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:09 +0200 Subject: usb: ohci-nxp: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-68-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-nxp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c index 5b32e683e367..c04b2af5c766 100644 --- a/drivers/usb/host/ohci-nxp.c +++ b/drivers/usb/host/ohci-nxp.c @@ -237,7 +237,7 @@ fail_disable: return ret; } -static int ohci_hcd_nxp_remove(struct platform_device *pdev) +static void ohci_hcd_nxp_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); @@ -246,8 +246,6 @@ static int ohci_hcd_nxp_remove(struct platform_device *pdev) usb_put_hcd(hcd); clk_disable_unprepare(usb_host_clk); isp1301_i2c_client = NULL; - - return 0; } /* work with hotplug and coldplug */ @@ -267,7 +265,7 @@ static struct platform_driver ohci_hcd_nxp_driver = { .of_match_table = of_match_ptr(ohci_hcd_nxp_match), }, .probe = ohci_hcd_nxp_probe, - .remove = ohci_hcd_nxp_remove, + .remove_new = ohci_hcd_nxp_remove, }; static int __init ohci_nxp_init(void) -- cgit v1.2.3 From e399d0147febd1fec002f7accdd97112bc3bc64f Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:10 +0200 Subject: usb: ohci-omap: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-69-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-omap.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index c82121602511..21a6f6c55e07 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -321,7 +321,7 @@ err_put_hcd: * the HCD's stop() method. It is always called from a thread * context, normally "rmmod", "apmd", or something similar. */ -static int ohci_hcd_omap_remove(struct platform_device *pdev) +static void ohci_hcd_omap_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct ohci_omap_priv *priv = hcd_to_ohci_omap_priv(hcd); @@ -340,7 +340,6 @@ static int ohci_hcd_omap_remove(struct platform_device *pdev) clk_unprepare(priv->usb_host_ck); clk_put(priv->usb_host_ck); usb_put_hcd(hcd); - return 0; } /*-------------------------------------------------------------------------*/ @@ -391,7 +390,7 @@ static int ohci_omap_resume(struct platform_device *dev) */ static struct platform_driver ohci_hcd_omap_driver = { .probe = ohci_hcd_omap_probe, - .remove = ohci_hcd_omap_remove, + .remove_new = ohci_hcd_omap_remove, .shutdown = usb_hcd_platform_shutdown, #ifdef CONFIG_PM .suspend = ohci_omap_suspend, -- cgit v1.2.3 From 18b93fc987c43523d9b1b87f6f12e4d9d2792944 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:11 +0200 Subject: usb: ohci-platform: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20230517230239.187727-70-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-platform.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index a84305091c43..03232c5936e8 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -239,7 +239,7 @@ err_put_clks: return err; } -static int ohci_platform_remove(struct platform_device *dev) +static void ohci_platform_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); @@ -264,8 +264,6 @@ static int ohci_platform_remove(struct platform_device *dev) if (pdata == &ohci_platform_defaults) dev->dev.platform_data = NULL; - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -347,7 +345,7 @@ static const struct dev_pm_ops ohci_platform_pm_ops = { static struct platform_driver ohci_platform_driver = { .id_table = ohci_platform_table, .probe = ohci_platform_probe, - .remove = ohci_platform_remove, + .remove_new = ohci_platform_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ohci-platform", -- cgit v1.2.3 From 7b0b81006ec64f38645462a56d9639acf1adad73 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:12 +0200 Subject: usb: ohci-ppc-of: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-71-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-ppc-of.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index f2f6c832ec98..35a7ad7e2569 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -176,7 +176,7 @@ err_rmr: return rv; } -static int ohci_hcd_ppc_of_remove(struct platform_device *op) +static void ohci_hcd_ppc_of_remove(struct platform_device *op) { struct usb_hcd *hcd = platform_get_drvdata(op); @@ -187,8 +187,6 @@ static int ohci_hcd_ppc_of_remove(struct platform_device *op) irq_dispose_mapping(hcd->irq); usb_put_hcd(hcd); - - return 0; } static const struct of_device_id ohci_hcd_ppc_of_match[] = { @@ -224,7 +222,7 @@ MODULE_DEVICE_TABLE(of, ohci_hcd_ppc_of_match); static struct platform_driver ohci_hcd_ppc_of_driver = { .probe = ohci_hcd_ppc_of_probe, - .remove = ohci_hcd_ppc_of_remove, + .remove_new = ohci_hcd_ppc_of_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "ppc-of-ohci", -- cgit v1.2.3 From 8c5f41ac18bb38505af9904837b90b51b6821f64 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:13 +0200 Subject: usb: ohci-pxa27x: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-72-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-pxa27x.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c index 0bc7e96bcc93..6f571c776d11 100644 --- a/drivers/usb/host/ohci-pxa27x.c +++ b/drivers/usb/host/ohci-pxa27x.c @@ -506,7 +506,7 @@ static int ohci_hcd_pxa27x_probe(struct platform_device *pdev) * the HCD's stop() method. It is always called from a thread * context, normally "rmmod", "apmd", or something similar. */ -static int ohci_hcd_pxa27x_remove(struct platform_device *pdev) +static void ohci_hcd_pxa27x_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd); @@ -519,7 +519,6 @@ static int ohci_hcd_pxa27x_remove(struct platform_device *pdev) pxa27x_ohci_set_vbus_power(pxa_ohci, i, false); usb_put_hcd(hcd); - return 0; } /*-------------------------------------------------------------------------*/ @@ -577,7 +576,7 @@ static const struct dev_pm_ops ohci_hcd_pxa27x_pm_ops = { static struct platform_driver ohci_hcd_pxa27x_driver = { .probe = ohci_hcd_pxa27x_probe, - .remove = ohci_hcd_pxa27x_remove, + .remove_new = ohci_hcd_pxa27x_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "pxa27x-ohci", -- cgit v1.2.3 From a0f2863ab384de2f6b784f0ca9c063868f560636 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:14 +0200 Subject: usb: ohci-s3c2410: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-73-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-s3c2410.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index 85a0a9ae0095..c5c9b4cbcb9a 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -329,7 +329,7 @@ static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc) * the HCD's stop() method. It is always called from a thread * context, normally "rmmod", "apmd", or something similar. */ -static int +static void ohci_hcd_s3c2410_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); @@ -337,7 +337,6 @@ ohci_hcd_s3c2410_remove(struct platform_device *dev) usb_remove_hcd(hcd); s3c2410_stop_hc(dev); usb_put_hcd(hcd); - return 0; } /* @@ -458,7 +457,7 @@ MODULE_DEVICE_TABLE(of, ohci_hcd_s3c2410_dt_ids); static struct platform_driver ohci_hcd_s3c2410_driver = { .probe = ohci_hcd_s3c2410_probe, - .remove = ohci_hcd_s3c2410_remove, + .remove_new = ohci_hcd_s3c2410_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "s3c2410-ohci", -- cgit v1.2.3 From 982366fc0716046c41f418817b8b64458f19558e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:15 +0200 Subject: usb: ohci-sm501: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-74-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-sm501.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index f5de586454e3..0468eeb4fcfd 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -185,7 +185,7 @@ err0: return retval; } -static int ohci_hcd_sm501_drv_remove(struct platform_device *pdev) +static void ohci_hcd_sm501_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct resource *mem; @@ -202,8 +202,6 @@ static int ohci_hcd_sm501_drv_remove(struct platform_device *pdev) sm501_modify_reg(pdev->dev.parent, SM501_IRQ_MASK, 0, 1 << 6); sm501_unit_power(pdev->dev.parent, SM501_GATE_USB_HOST, 0); - - return 0; } /*-------------------------------------------------------------------------*/ @@ -255,7 +253,7 @@ static int ohci_sm501_resume(struct platform_device *pdev) */ static struct platform_driver ohci_hcd_sm501_driver = { .probe = ohci_hcd_sm501_drv_probe, - .remove = ohci_hcd_sm501_drv_remove, + .remove_new = ohci_hcd_sm501_drv_remove, .shutdown = usb_hcd_platform_shutdown, .suspend = ohci_sm501_suspend, .resume = ohci_sm501_resume, -- cgit v1.2.3 From 106f477c08820b473e200f14187d65deef28ed95 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:16 +0200 Subject: usb: ohci-spear: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-75-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-spear.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index 196951a27f3f..f4b2656407dd 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -98,7 +98,7 @@ fail: return retval; } -static int spear_ohci_hcd_drv_remove(struct platform_device *pdev) +static void spear_ohci_hcd_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct spear_ohci *sohci_p = to_spear_ohci(hcd); @@ -108,7 +108,6 @@ static int spear_ohci_hcd_drv_remove(struct platform_device *pdev) clk_disable_unprepare(sohci_p->clk); usb_put_hcd(hcd); - return 0; } #if defined(CONFIG_PM) @@ -159,7 +158,7 @@ MODULE_DEVICE_TABLE(of, spear_ohci_id_table); /* Driver definition to register with the platform bus */ static struct platform_driver spear_ohci_hcd_driver = { .probe = spear_ohci_hcd_drv_probe, - .remove = spear_ohci_hcd_drv_remove, + .remove_new = spear_ohci_hcd_drv_remove, #ifdef CONFIG_PM .suspend = spear_ohci_hcd_drv_suspend, .resume = spear_ohci_hcd_drv_resume, -- cgit v1.2.3 From 1a232291741cf7d80f8cdd40e7eb5c869001b65b Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:17 +0200 Subject: usb: ohci-st: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Patrice Chotard Link: https://lore.kernel.org/r/20230517230239.187727-76-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-st.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/ohci-st.c b/drivers/usb/host/ohci-st.c index 82eef3c62e11..884e447a8098 100644 --- a/drivers/usb/host/ohci-st.c +++ b/drivers/usb/host/ohci-st.c @@ -233,7 +233,7 @@ err_put_hcd: return err; } -static int st_ohci_platform_remove(struct platform_device *dev) +static void st_ohci_platform_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); @@ -253,8 +253,6 @@ static int st_ohci_platform_remove(struct platform_device *dev) if (pdata == &ohci_platform_defaults) dev->dev.platform_data = NULL; - - return 0; } #ifdef CONFIG_PM_SLEEP @@ -306,7 +304,7 @@ MODULE_DEVICE_TABLE(of, st_ohci_platform_ids); static struct platform_driver ohci_platform_driver = { .probe = st_ohci_platform_probe, - .remove = st_ohci_platform_remove, + .remove_new = st_ohci_platform_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "st-ohci", -- cgit v1.2.3 From aae652bc7b47f08bf08ab78d6a276415cc35c677 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:18 +0200 Subject: usb: oxu210hp-hcd: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-77-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/oxu210hp-hcd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index f998d3f1a78a..50c1ccabb0f5 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -4278,14 +4278,12 @@ static void oxu_remove(struct platform_device *pdev, struct usb_hcd *hcd) usb_put_hcd(hcd); } -static int oxu_drv_remove(struct platform_device *pdev) +static void oxu_drv_remove(struct platform_device *pdev) { struct oxu_info *info = platform_get_drvdata(pdev); oxu_remove(pdev, info->hcd[0]); oxu_remove(pdev, info->hcd[1]); - - return 0; } static void oxu_drv_shutdown(struct platform_device *pdev) @@ -4317,7 +4315,7 @@ static int oxu_drv_resume(struct device *dev) static struct platform_driver oxu_driver = { .probe = oxu_drv_probe, - .remove = oxu_drv_remove, + .remove_new = oxu_drv_remove, .shutdown = oxu_drv_shutdown, .suspend = oxu_drv_suspend, .resume = oxu_drv_resume, -- cgit v1.2.3 From 90995d53ab35ec304d04a4e446d9fb97841d3a1b Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:19 +0200 Subject: usb: r8a66597-hcd: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-78-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/r8a66597-hcd.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index abb88dd40d4e..9f4bf8c5f8a5 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -2379,7 +2379,7 @@ static const struct dev_pm_ops r8a66597_dev_pm_ops = { #define R8A66597_DEV_PM_OPS NULL #endif -static int r8a66597_remove(struct platform_device *pdev) +static void r8a66597_remove(struct platform_device *pdev) { struct r8a66597 *r8a66597 = platform_get_drvdata(pdev); struct usb_hcd *hcd = r8a66597_to_hcd(r8a66597); @@ -2390,7 +2390,6 @@ static int r8a66597_remove(struct platform_device *pdev) if (r8a66597->pdata->on_chip) clk_put(r8a66597->clk); usb_put_hcd(hcd); - return 0; } static int r8a66597_probe(struct platform_device *pdev) @@ -2511,7 +2510,7 @@ clean_up: static struct platform_driver r8a66597_driver = { .probe = r8a66597_probe, - .remove = r8a66597_remove, + .remove_new = r8a66597_remove, .driver = { .name = hcd_name, .pm = R8A66597_DEV_PM_OPS, -- cgit v1.2.3 From 32dbe25eee71a58a94c15cfd62de08d7cf6adb8e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:20 +0200 Subject: usb: sl811-hcd: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-79-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/sl811-hcd.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index b8b90eec9107..0956495bba57 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1579,7 +1579,7 @@ static const struct hc_driver sl811h_hc_driver = { /*-------------------------------------------------------------------------*/ -static int +static void sl811h_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); @@ -1599,7 +1599,6 @@ sl811h_remove(struct platform_device *dev) iounmap(sl811->addr_reg); usb_put_hcd(hcd); - return 0; } static int @@ -1783,7 +1782,7 @@ sl811h_resume(struct platform_device *dev) /* this driver is exported so sl811_cs can depend on it */ struct platform_driver sl811h_driver = { .probe = sl811h_probe, - .remove = sl811h_remove, + .remove_new = sl811h_remove, .suspend = sl811h_suspend, .resume = sl811h_resume, -- cgit v1.2.3 From 18cb7c4d53d061a56bd76b74e3a8a7f4747d3dad Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:21 +0200 Subject: usb: uhci-grlib: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-80-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-grlib.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c index 907d5f01edfd..ac3fc5970315 100644 --- a/drivers/usb/host/uhci-grlib.c +++ b/drivers/usb/host/uhci-grlib.c @@ -147,7 +147,7 @@ err_usb: return rv; } -static int uhci_hcd_grlib_remove(struct platform_device *op) +static void uhci_hcd_grlib_remove(struct platform_device *op) { struct usb_hcd *hcd = platform_get_drvdata(op); @@ -157,8 +157,6 @@ static int uhci_hcd_grlib_remove(struct platform_device *op) irq_dispose_mapping(hcd->irq); usb_put_hcd(hcd); - - return 0; } /* Make sure the controller is quiescent and that we're not using it @@ -185,7 +183,7 @@ MODULE_DEVICE_TABLE(of, uhci_hcd_grlib_of_match); static struct platform_driver uhci_grlib_driver = { .probe = uhci_hcd_grlib_probe, - .remove = uhci_hcd_grlib_remove, + .remove_new = uhci_hcd_grlib_remove, .shutdown = uhci_hcd_grlib_shutdown, .driver = { .name = "grlib-uhci", -- cgit v1.2.3 From f0c8aa5c8dca41db6dc6905a1663cd4eeaa8856c Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:22 +0200 Subject: usb: uhci-platform: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-81-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-platform.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index b2049b47a08d..71ca532fc086 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -152,7 +152,7 @@ err_rmr: return ret; } -static int uhci_hcd_platform_remove(struct platform_device *pdev) +static void uhci_hcd_platform_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); struct uhci_hcd *uhci = hcd_to_uhci(hcd); @@ -160,8 +160,6 @@ static int uhci_hcd_platform_remove(struct platform_device *pdev) clk_disable_unprepare(uhci->clk); usb_remove_hcd(hcd); usb_put_hcd(hcd); - - return 0; } /* Make sure the controller is quiescent and that we're not using it @@ -187,7 +185,7 @@ MODULE_DEVICE_TABLE(of, platform_uhci_ids); static struct platform_driver uhci_platform_driver = { .probe = uhci_hcd_platform_probe, - .remove = uhci_hcd_platform_remove, + .remove_new = uhci_hcd_platform_remove, .shutdown = uhci_hcd_platform_shutdown, .driver = { .name = "platform-uhci", -- cgit v1.2.3 From e0d53e4f7fd84cb41ece77c3f02c18699cc82736 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:27 +0200 Subject: usb: isp1760: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Rui Miguel Silva Link: https://lore.kernel.org/r/20230517230239.187727-86-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/isp1760/isp1760-if.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/isp1760/isp1760-if.c b/drivers/usb/isp1760/isp1760-if.c index 65ba5aca2a4f..fe1e3985419a 100644 --- a/drivers/usb/isp1760/isp1760-if.c +++ b/drivers/usb/isp1760/isp1760-if.c @@ -246,11 +246,9 @@ static int isp1760_plat_probe(struct platform_device *pdev) return 0; } -static int isp1760_plat_remove(struct platform_device *pdev) +static void isp1760_plat_remove(struct platform_device *pdev) { isp1760_unregister(&pdev->dev); - - return 0; } #ifdef CONFIG_OF @@ -265,7 +263,7 @@ MODULE_DEVICE_TABLE(of, isp1760_of_match); static struct platform_driver isp1760_plat_driver = { .probe = isp1760_plat_probe, - .remove = isp1760_plat_remove, + .remove_new = isp1760_plat_remove, .driver = { .name = "isp1760", .of_match_table = of_match_ptr(isp1760_of_match), -- cgit v1.2.3 From b6b64b67c8ac08f370338cd1f8e807487043a400 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:28 +0200 Subject: usb: misc: eud: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Konrad Dybcio Reviewed-by: Bhupesh Sharma Link: https://lore.kernel.org/r/20230517230239.187727-87-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/qcom_eud.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c index b7f13df00764..0dc414463759 100644 --- a/drivers/usb/misc/qcom_eud.c +++ b/drivers/usb/misc/qcom_eud.c @@ -217,7 +217,7 @@ static int eud_probe(struct platform_device *pdev) return 0; } -static int eud_remove(struct platform_device *pdev) +static void eud_remove(struct platform_device *pdev) { struct eud_chip *chip = platform_get_drvdata(pdev); @@ -226,8 +226,6 @@ static int eud_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, false); disable_irq_wake(chip->irq); - - return 0; } static const struct of_device_id eud_dt_match[] = { @@ -238,7 +236,7 @@ MODULE_DEVICE_TABLE(of, eud_dt_match); static struct platform_driver eud_driver = { .probe = eud_probe, - .remove = eud_remove, + .remove_new = eud_remove, .driver = { .name = "qcom_eud", .dev_groups = eud_groups, -- cgit v1.2.3 From 81a7d006ed174b6697192552422da781c6551363 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:29 +0200 Subject: usb: misc: usb3503: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-88-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb3503.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index c6cfd1edaf76..fa3005934942 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -335,14 +335,12 @@ static int usb3503_platform_probe(struct platform_device *pdev) return usb3503_probe(hub); } -static int usb3503_platform_remove(struct platform_device *pdev) +static void usb3503_platform_remove(struct platform_device *pdev) { struct usb3503 *hub; hub = platform_get_drvdata(pdev); clk_disable_unprepare(hub->clk); - - return 0; } static int __maybe_unused usb3503_suspend(struct usb3503 *hub) @@ -425,7 +423,7 @@ static struct platform_driver usb3503_platform_driver = { .pm = pm_ptr(&usb3503_platform_pm_ops), }, .probe = usb3503_platform_probe, - .remove = usb3503_platform_remove, + .remove_new = usb3503_platform_remove, }; static int __init usb3503_init(void) -- cgit v1.2.3 From 456a91ce7de4b9157fd5013c1e4dd8dd3c6daccb Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:30 +0200 Subject: usb: renesas_usbhs: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Yoshihiro Shimoda Link: https://lore.kernel.org/r/20230517230239.187727-89-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/renesas_usbhs/common.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index fa34efabcccf..111b7ee152c4 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -762,7 +762,7 @@ probe_end_pipe_exit: return ret; } -static int usbhs_remove(struct platform_device *pdev) +static void usbhs_remove(struct platform_device *pdev) { struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); @@ -780,8 +780,6 @@ static int usbhs_remove(struct platform_device *pdev) usbhs_mod_remove(priv); usbhs_fifo_remove(priv); usbhs_pipe_remove(priv); - - return 0; } static __maybe_unused int usbhsc_suspend(struct device *dev) @@ -826,7 +824,7 @@ static struct platform_driver renesas_usbhs_driver = { .of_match_table = usbhs_of_match, }, .probe = usbhs_probe, - .remove = usbhs_remove, + .remove_new = usbhs_remove, }; module_platform_driver(renesas_usbhs_driver); -- cgit v1.2.3 From 61b013f9e1f7b82a5fd45949ce8d3ac3874cc287 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:31 +0200 Subject: usb: roles: intel_xhci: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517230239.187727-90-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/roles/intel-xhci-usb-role-switch.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c index 5c96e929acea..e5c6c413a075 100644 --- a/drivers/usb/roles/intel-xhci-usb-role-switch.c +++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c @@ -195,7 +195,7 @@ static int intel_xhci_usb_probe(struct platform_device *pdev) return 0; } -static int intel_xhci_usb_remove(struct platform_device *pdev) +static void intel_xhci_usb_remove(struct platform_device *pdev) { struct intel_xhci_usb_data *data = platform_get_drvdata(pdev); @@ -203,8 +203,6 @@ static int intel_xhci_usb_remove(struct platform_device *pdev) usb_role_switch_unregister(data->role_sw); fwnode_handle_put(software_node_fwnode(&intel_xhci_usb_node)); - - return 0; } static const struct platform_device_id intel_xhci_usb_table[] = { @@ -219,7 +217,7 @@ static struct platform_driver intel_xhci_usb_driver = { }, .id_table = intel_xhci_usb_table, .probe = intel_xhci_usb_probe, - .remove = intel_xhci_usb_remove, + .remove_new = intel_xhci_usb_remove, }; module_platform_driver(intel_xhci_usb_driver); -- cgit v1.2.3 From 08cfceeeea1eebfe70b02d8f411398347a9a711d Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:32 +0200 Subject: usb: typec: mux: gpio-sbu: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Bjorn Andersson Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230517230239.187727-91-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/gpio-sbu-mux.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/mux/gpio-sbu-mux.c b/drivers/usb/typec/mux/gpio-sbu-mux.c index c07856069d43..ad60fd4e8431 100644 --- a/drivers/usb/typec/mux/gpio-sbu-mux.c +++ b/drivers/usb/typec/mux/gpio-sbu-mux.c @@ -137,7 +137,7 @@ static int gpio_sbu_mux_probe(struct platform_device *pdev) return 0; } -static int gpio_sbu_mux_remove(struct platform_device *pdev) +static void gpio_sbu_mux_remove(struct platform_device *pdev) { struct gpio_sbu_mux *sbu_mux = platform_get_drvdata(pdev); @@ -145,8 +145,6 @@ static int gpio_sbu_mux_remove(struct platform_device *pdev) typec_mux_unregister(sbu_mux->mux); typec_switch_unregister(sbu_mux->sw); - - return 0; } static const struct of_device_id gpio_sbu_mux_match[] = { @@ -157,7 +155,7 @@ MODULE_DEVICE_TABLE(of, gpio_sbu_mux_match); static struct platform_driver gpio_sbu_mux_driver = { .probe = gpio_sbu_mux_probe, - .remove = gpio_sbu_mux_remove, + .remove_new = gpio_sbu_mux_remove, .driver = { .name = "gpio_sbu_mux", .of_match_table = gpio_sbu_mux_match, -- cgit v1.2.3 From ef0a3642b320c96adfd7951db35f7f0a0a535d39 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:33 +0200 Subject: usb: typec: intel_pmc_mux: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230517230239.187727-92-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/intel_pmc_mux.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index 34e4188a40ff..e049eadb591e 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -706,7 +706,7 @@ err_remove_ports: return ret; } -static int pmc_usb_remove(struct platform_device *pdev) +static void pmc_usb_remove(struct platform_device *pdev) { struct pmc_usb *pmc = platform_get_drvdata(pdev); int i; @@ -718,8 +718,6 @@ static int pmc_usb_remove(struct platform_device *pdev) } acpi_dev_put(pmc->iom_adev); - - return 0; } static const struct acpi_device_id pmc_usb_acpi_ids[] = { @@ -734,7 +732,7 @@ static struct platform_driver pmc_usb_driver = { .acpi_match_table = ACPI_PTR(pmc_usb_acpi_ids), }, .probe = pmc_usb_probe, - .remove = pmc_usb_remove, + .remove_new = pmc_usb_remove, }; module_platform_driver(pmc_usb_driver); -- cgit v1.2.3 From 42c78cfa003dd3f6c86d9baa7f167ed085f773e7 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:35 +0200 Subject: usb: typec: tcpci_mt6360: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Guenter Roeck Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230517230239.187727-94-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpci_mt6360.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpci_mt6360.c b/drivers/usb/typec/tcpm/tcpci_mt6360.c index 6fa8fd5c8041..02b7fd302265 100644 --- a/drivers/usb/typec/tcpm/tcpci_mt6360.c +++ b/drivers/usb/typec/tcpm/tcpci_mt6360.c @@ -178,13 +178,12 @@ static int mt6360_tcpc_probe(struct platform_device *pdev) return 0; } -static int mt6360_tcpc_remove(struct platform_device *pdev) +static void mt6360_tcpc_remove(struct platform_device *pdev) { struct mt6360_tcpc_info *mti = platform_get_drvdata(pdev); disable_irq(mti->irq); tcpci_unregister_port(mti->tcpci); - return 0; } static int __maybe_unused mt6360_tcpc_suspend(struct device *dev) @@ -222,7 +221,7 @@ static struct platform_driver mt6360_tcpc_driver = { .of_match_table = mt6360_tcpc_of_id, }, .probe = mt6360_tcpc_probe, - .remove = mt6360_tcpc_remove, + .remove_new = mt6360_tcpc_remove, }; module_platform_driver(mt6360_tcpc_driver); -- cgit v1.2.3 From 529ae3fe7b5c8fbab06d785bdf804d2ac8bf06a9 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:36 +0200 Subject: usb: typec: tcpci_mt6360: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Heikki Krogerus Acked-by: Guenter Roeck Link: https://lore.kernel.org/r/20230517230239.187727-95-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpci_mt6370.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpci_mt6370.c b/drivers/usb/typec/tcpm/tcpci_mt6370.c index c5bb201a5163..2a079464b398 100644 --- a/drivers/usb/typec/tcpm/tcpci_mt6370.c +++ b/drivers/usb/typec/tcpm/tcpci_mt6370.c @@ -178,12 +178,10 @@ static int mt6370_tcpc_probe(struct platform_device *pdev) return 0; } -static int mt6370_tcpc_remove(struct platform_device *pdev) +static void mt6370_tcpc_remove(struct platform_device *pdev) { dev_pm_clear_wake_irq(&pdev->dev); device_init_wakeup(&pdev->dev, false); - - return 0; } static const struct of_device_id mt6370_tcpc_devid_table[] = { @@ -198,7 +196,7 @@ static struct platform_driver mt6370_tcpc_driver = { .of_match_table = mt6370_tcpc_devid_table, }, .probe = mt6370_tcpc_probe, - .remove = mt6370_tcpc_remove, + .remove_new = mt6370_tcpc_remove, }; module_platform_driver(mt6370_tcpc_driver); -- cgit v1.2.3 From 72d70bf73cfc616ad5f439f57b215cbbe69ec3e0 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:37 +0200 Subject: usb: typec: wcove: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Guenter Roeck Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230517230239.187727-96-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/wcove.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/tcpm/wcove.c b/drivers/usb/typec/tcpm/wcove.c index 20917d85d6f4..87d4abde0ea2 100644 --- a/drivers/usb/typec/tcpm/wcove.c +++ b/drivers/usb/typec/tcpm/wcove.c @@ -671,7 +671,7 @@ static int wcove_typec_probe(struct platform_device *pdev) return 0; } -static int wcove_typec_remove(struct platform_device *pdev) +static void wcove_typec_remove(struct platform_device *pdev) { struct wcove_typec *wcove = platform_get_drvdata(pdev); unsigned int val; @@ -684,8 +684,6 @@ static int wcove_typec_remove(struct platform_device *pdev) tcpm_unregister_port(wcove->tcpm); fwnode_remove_software_node(wcove->tcpc.fwnode); - - return 0; } static struct platform_driver wcove_typec_driver = { @@ -693,7 +691,7 @@ static struct platform_driver wcove_typec_driver = { .name = "bxt_wcove_usbc", }, .probe = wcove_typec_probe, - .remove = wcove_typec_remove, + .remove_new = wcove_typec_remove, }; module_platform_driver(wcove_typec_driver); -- cgit v1.2.3 From fc4ecc0cd5618759094ad0ed8a654789b15cb4e7 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:38 +0200 Subject: usb: typec: ucsi: acpi: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230517230239.187727-97-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi_acpi.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c index 217355f1f9b9..6bbf490ac401 100644 --- a/drivers/usb/typec/ucsi/ucsi_acpi.c +++ b/drivers/usb/typec/ucsi/ucsi_acpi.c @@ -212,7 +212,7 @@ static int ucsi_acpi_probe(struct platform_device *pdev) return 0; } -static int ucsi_acpi_remove(struct platform_device *pdev) +static void ucsi_acpi_remove(struct platform_device *pdev) { struct ucsi_acpi *ua = platform_get_drvdata(pdev); @@ -221,8 +221,6 @@ static int ucsi_acpi_remove(struct platform_device *pdev) acpi_remove_notify_handler(ACPI_HANDLE(&pdev->dev), ACPI_DEVICE_NOTIFY, ucsi_acpi_notify); - - return 0; } static int ucsi_acpi_resume(struct device *dev) @@ -247,7 +245,7 @@ static struct platform_driver ucsi_acpi_platform_driver = { .acpi_match_table = ACPI_PTR(ucsi_acpi_match), }, .probe = ucsi_acpi_probe, - .remove = ucsi_acpi_remove, + .remove_new = ucsi_acpi_remove, }; module_platform_driver(ucsi_acpi_platform_driver); -- cgit v1.2.3 From 19b3cf44e18c202d696354d1947b9a74fbad046e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 01:02:39 +0200 Subject: usbip: vhci_hcd: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Acked-by: Shuah Khan Link: https://lore.kernel.org/r/20230517230239.187727-98-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/vhci_hcd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index 233265550fc6..37d1fc34e8a5 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -1393,7 +1393,7 @@ put_usb2_hcd: return ret; } -static int vhci_hcd_remove(struct platform_device *pdev) +static void vhci_hcd_remove(struct platform_device *pdev) { struct vhci *vhci = *((void **)dev_get_platdata(&pdev->dev)); @@ -1410,8 +1410,6 @@ static int vhci_hcd_remove(struct platform_device *pdev) vhci->vhci_hcd_hs = NULL; vhci->vhci_hcd_ss = NULL; - - return 0; } #ifdef CONFIG_PM @@ -1485,7 +1483,7 @@ static int vhci_hcd_resume(struct platform_device *pdev) static struct platform_driver vhci_driver = { .probe = vhci_hcd_probe, - .remove = vhci_hcd_remove, + .remove_new = vhci_hcd_remove, .suspend = vhci_hcd_suspend, .resume = vhci_hcd_resume, .driver = { -- cgit v1.2.3 From b4a4be8471846d96b0ac52a0e9e7d48005cc97e2 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Sun, 28 May 2023 18:48:12 +0300 Subject: USB: fix up merge of 6.4-rc4 into usb-next The merge of 6.4-rc4 got the changes in drivers/usb/dwc3/gadget.c completely incorrect, so fix it up properly. Link: https://lore.kernel.org/r/f604f836-7858-6140-4ec1-9ba95cba6991@kernel.org Fixes: 7e530d32a365 ("Merge 6.4-rc4 into usb-next") Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 7d59e0f43fda..578804dc29ca 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2702,13 +2702,17 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc) static int dwc3_gadget_soft_connect(struct dwc3 *dwc) { + int ret; + /* * In the Synopsys DWC_usb31 1.90a programming guide section * 4.1.9, it specifies that for a reconnect after a * device-initiated disconnect requires a core soft reset * (DCTL.CSftRst) before enabling the run/stop bit. */ - dwc3_core_soft_reset(dwc); + ret = dwc3_core_soft_reset(dwc); + if (ret) + return ret; dwc3_event_buffers_setup(dwc); __dwc3_gadget_start(dwc); @@ -2753,25 +2757,11 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) synchronize_irq(dwc->irq_gadget); - if (!is_on) { + if (!is_on) ret = dwc3_gadget_soft_disconnect(dwc); - } else { - /* - * In the Synopsys DWC_usb31 1.90a programming guide section - * 4.1.9, it specifies that for a reconnect after a - * device-initiated disconnect requires a core soft reset - * (DCTL.CSftRst) before enabling the run/stop bit. - */ - ret = dwc3_core_soft_reset(dwc); - if (ret) - goto done; - - dwc3_event_buffers_setup(dwc); - __dwc3_gadget_start(dwc); - ret = dwc3_gadget_run_stop(dwc, true); - } + else + ret = dwc3_gadget_soft_connect(dwc); -done: pm_runtime_put(dwc->dev); return ret; -- cgit v1.2.3 From 0c7f35d26b1dc45ab27e3ea8ff7f6a8a88a18174 Mon Sep 17 00:00:00 2001 From: Galen Guyer Date: Mon, 8 May 2023 17:21:20 -0400 Subject: usbip: give a more helpful error message if vhdi_hcd isn't loaded Suggest loading vhdi_hcd if it's not loaded to make error message less opaque Signed-off-by: Galen Guyer Reviewed-By: Hongren Zheng Acked-by: Shuah Khan Link: https://lore.kernel.org/r/20230508212120.435329-1-galen@galenguyer.com Signed-off-by: Greg Kroah-Hartman --- tools/usb/usbip/src/usbip_attach.c | 2 +- tools/usb/usbip/src/usbip_detach.c | 2 +- tools/usb/usbip/src/usbip_port.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/usb/usbip/src/usbip_attach.c b/tools/usb/usbip/src/usbip_attach.c index b4aeb9f1f493..531a415538f9 100644 --- a/tools/usb/usbip/src/usbip_attach.c +++ b/tools/usb/usbip/src/usbip_attach.c @@ -86,7 +86,7 @@ static int import_device(int sockfd, struct usbip_usb_device *udev) rc = usbip_vhci_driver_open(); if (rc < 0) { - err("open vhci_driver"); + err("open vhci_driver (is vhci_hcd loaded?)"); goto err_out; } diff --git a/tools/usb/usbip/src/usbip_detach.c b/tools/usb/usbip/src/usbip_detach.c index aec993159036..b29101986b5a 100644 --- a/tools/usb/usbip/src/usbip_detach.c +++ b/tools/usb/usbip/src/usbip_detach.c @@ -50,7 +50,7 @@ static int detach_port(char *port) ret = usbip_vhci_driver_open(); if (ret < 0) { - err("open vhci_driver"); + err("open vhci_driver (is vhci_hcd loaded?)"); return -1; } diff --git a/tools/usb/usbip/src/usbip_port.c b/tools/usb/usbip/src/usbip_port.c index 4d14387df13d..21a20e378419 100644 --- a/tools/usb/usbip/src/usbip_port.c +++ b/tools/usb/usbip/src/usbip_port.c @@ -18,7 +18,7 @@ static int list_imported_devices(void) ret = usbip_vhci_driver_open(); if (ret < 0) { - err("open vhci_driver"); + err("open vhci_driver (is vhci_hcd loaded?)"); goto err_names_free; } -- cgit v1.2.3 From 06042d7b32a71c6a423948f8c7fd4bd13572bdf3 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 3 May 2023 19:36:22 +0200 Subject: usbip: Use _FORTIFY_SOURCE=2 instead of (implicitly) =1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _FORTIFY_SOURCE=2 uses more and stricter checks. This is what e.g. Debian recommends to build packages with. While at it fix a typo in the output of ./configure --help. Signed-off-by: Uwe Kleine-König Reviewed-By: Hongren Zheng Link: https://lore.kernel.org/r/20230503173622.1072787-1-ukleinek@debian.org Signed-off-by: Greg Kroah-Hartman --- tools/usb/usbip/configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/usb/usbip/configure.ac b/tools/usb/usbip/configure.ac index 607d05c5ccfd..8debf934f8b7 100644 --- a/tools/usb/usbip/configure.ac +++ b/tools/usb/usbip/configure.ac @@ -94,11 +94,11 @@ AC_SUBST([USBIDS_DIR]) AC_MSG_CHECKING([whether to use fortify]) AC_ARG_WITH([fortify], [AS_HELP_STRING([--with-fortify], - [use _FORTIFY_SROUCE option when compiling)])], + [use _FORTIFY_SOURCE=2 option when compiling)])], dnl [ACTION-IF-GIVEN] [if test "$withval" = "yes"; then AC_MSG_RESULT([yes]) - CFLAGS="$CFLAGS -D_FORTIFY_SOURCE -O" + CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=2 -O" else AC_MSG_RESULT([no]) CFLAGS="$CFLAGS -U_FORTIFY_SOURCE" -- cgit v1.2.3 From c722576b2454c44934e27bd73fdf828e720fb237 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Mon, 15 May 2023 07:40:43 -0400 Subject: usb: typec: qcom: set pm8150b_typec_res storage-class-specifier to static smatch reports drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c:323:29: warning: symbol 'pm8150b_typec_res' was not declared. Should it be static? This variable is only used in its defining file, so it should be static Signed-off-by: Tom Rix Reviewed-by: Bryan O'Donoghue Reviewed-by: Guenter Roeck Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230515114043.3452010-1-trix@redhat.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c index 191458ce4a06..937e855a6c4c 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c @@ -320,7 +320,7 @@ static struct pmic_typec_port_resources pm8150b_port_res = { .nr_irqs = 7, }; -struct pmic_typec_resources pm8150b_typec_res = { +static struct pmic_typec_resources pm8150b_typec_res = { .pdphy_res = &pm8150b_pdphy_res, .port_res = &pm8150b_port_res, }; -- cgit v1.2.3 From 097fb3ee710d4de83b8d4f5589e8ee13e0f0541e Mon Sep 17 00:00:00 2001 From: Vladislav Efanov Date: Wed, 17 May 2023 20:25:18 +0300 Subject: usb: dwc3: qcom: Fix potential memory leak Function dwc3_qcom_probe() allocates memory for resource structure which is pointed by parent_res pointer. This memory is not freed. This leads to memory leak. Use stack memory to prevent memory leak. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 2bc02355f8ba ("usb: dwc3: qcom: Add support for booting with ACPI") Signed-off-by: Vladislav Efanov Acked-by: Shawn Guo Link: https://lore.kernel.org/r/20230517172518.442591-1-VEfanov@ispras.ru Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-qcom.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 167f851c8e59..822735814050 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -791,6 +791,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct dwc3_qcom *qcom; struct resource *res, *parent_res = NULL; + struct resource local_res; int ret, i; bool ignore_pipe_clk; bool wakeup_source; @@ -842,9 +843,8 @@ static int dwc3_qcom_probe(struct platform_device *pdev) if (np) { parent_res = res; } else { - parent_res = kmemdup(res, sizeof(struct resource), GFP_KERNEL); - if (!parent_res) - return -ENOMEM; + memcpy(&local_res, res, sizeof(struct resource)); + parent_res = &local_res; parent_res->start = res->start + qcom->acpi_pdata->qscratch_base_offset; -- cgit v1.2.3 From 7b7efc925042ef72f8a64a14226a76e8c98c7732 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Thu, 18 May 2023 11:11:50 -0500 Subject: usb: typec: ucsi: Mark dGPUs as DEVICE scope power_supply_is_system_supplied() checks whether any power supplies are present that aren't batteries to decide whether the system is running on DC or AC. Downstream drivers use this to make performance decisions. Navi dGPUs include an UCSI function that has been exported since commit 17631e8ca2d3 ("i2c: designware: Add driver support for AMD NAVI GPU"). This UCSI function registers a power supply since commit 992a60ed0d5e ("usb: typec: ucsi: register with power_supply class") but this is not a system power supply. As the power supply for a dGPU is only for powering devices connected to dGPU, create a device property to indicate that the UCSI endpoint is only for the scope of `POWER_SUPPLY_SCOPE_DEVICE`. Link: https://lore.kernel.org/lkml/20230516182541.5836-2-mario.limonciello@amd.com/ Reviewed-by: Evan Quan Tested-by: Evan Quan Signed-off-by: Mario Limonciello Reviewed-by: Heikki Krogerus Reviewed-by: Sebastian Reichel Link: https://lore.kernel.org/r/20230518161150.92959-1-mario.limonciello@amd.com Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-designware-pcidrv.c | 13 ++++++++++++- drivers/i2c/busses/i2c-nvidia-gpu.c | 3 +++ drivers/usb/typec/ucsi/psy.c | 14 ++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index 782fe1ef3ca1..61d7a27aa070 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -234,6 +235,16 @@ static const struct dev_pm_ops i2c_dw_pm_ops = { SET_RUNTIME_PM_OPS(i2c_dw_pci_runtime_suspend, i2c_dw_pci_runtime_resume, NULL) }; +static const struct property_entry dgpu_properties[] = { + /* USB-C doesn't power the system */ + PROPERTY_ENTRY_U8("scope", POWER_SUPPLY_SCOPE_DEVICE), + {} +}; + +static const struct software_node dgpu_node = { + .properties = dgpu_properties, +}; + static int i2c_dw_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -325,7 +336,7 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev, } if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) { - dev->slave = i2c_new_ccgx_ucsi(&dev->adapter, dev->irq, NULL); + dev->slave = i2c_new_ccgx_ucsi(&dev->adapter, dev->irq, &dgpu_node); if (IS_ERR(dev->slave)) return dev_err_probe(dev->dev, PTR_ERR(dev->slave), "register UCSI failed\n"); diff --git a/drivers/i2c/busses/i2c-nvidia-gpu.c b/drivers/i2c/busses/i2c-nvidia-gpu.c index a8b99e7f6262..26622d24bb1b 100644 --- a/drivers/i2c/busses/i2c-nvidia-gpu.c +++ b/drivers/i2c/busses/i2c-nvidia-gpu.c @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -261,6 +262,8 @@ MODULE_DEVICE_TABLE(pci, gpu_i2c_ids); static const struct property_entry ccgx_props[] = { /* Use FW built for NVIDIA GPU only */ PROPERTY_ENTRY_STRING("firmware-name", "nvidia,gpu"), + /* USB-C doesn't power the system */ + PROPERTY_ENTRY_U8("scope", POWER_SUPPLY_SCOPE_DEVICE), { } }; diff --git a/drivers/usb/typec/ucsi/psy.c b/drivers/usb/typec/ucsi/psy.c index 56bf56517f75..384b42267f1f 100644 --- a/drivers/usb/typec/ucsi/psy.c +++ b/drivers/usb/typec/ucsi/psy.c @@ -27,8 +27,20 @@ static enum power_supply_property ucsi_psy_props[] = { POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_SCOPE, }; +static int ucsi_psy_get_scope(struct ucsi_connector *con, + union power_supply_propval *val) +{ + u8 scope = POWER_SUPPLY_SCOPE_UNKNOWN; + struct device *dev = con->ucsi->dev; + + device_property_read_u8(dev, "scope", &scope); + val->intval = scope; + return 0; +} + static int ucsi_psy_get_online(struct ucsi_connector *con, union power_supply_propval *val) { @@ -194,6 +206,8 @@ static int ucsi_psy_get_prop(struct power_supply *psy, return ucsi_psy_get_current_max(con, val); case POWER_SUPPLY_PROP_CURRENT_NOW: return ucsi_psy_get_current_now(con, val); + case POWER_SUPPLY_PROP_SCOPE: + return ucsi_psy_get_scope(con, val); default: return -EINVAL; } -- cgit v1.2.3 From 3c90c5a7fd425daaa62c4f82b4699aabd8e4edaa Mon Sep 17 00:00:00 2001 From: Min-Hua Chen Date: Wed, 24 May 2023 00:23:12 +0800 Subject: usb: typec: ucsi: correctly access opcode hdr->opcode is __le32 type, use le32_to_cpu() to cast opcode to integer in the switch..case statement to fix the following sparse warnings: drivers/usb/typec/ucsi/ucsi_glink.c:248:20: sparse: warning: restricted __le32 degrades to integer drivers/usb/typec/ucsi/ucsi_glink.c:248:20: sparse: warning: restricted __le32 degrades to integer drivers/usb/typec/ucsi/ucsi_glink.c:248:20: sparse: warning: restricted __le32 degrades to integer No functional change. Signed-off-by: Min-Hua Chen Reviewed-by: Heikki Krogerus Link: https://lore.kernel.org/r/20230523162314.114274-1-minhuadotchen@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi_glink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c index b454a5159896..1fe9cb5b6bd9 100644 --- a/drivers/usb/typec/ucsi/ucsi_glink.c +++ b/drivers/usb/typec/ucsi/ucsi_glink.c @@ -245,7 +245,7 @@ static void pmic_glink_ucsi_callback(const void *data, size_t len, void *priv) struct pmic_glink_ucsi *ucsi = priv; const struct pmic_glink_hdr *hdr = data; - switch (hdr->opcode) { + switch (le32_to_cpu(hdr->opcode)) { case UC_UCSI_READ_BUF_REQ: pmic_glink_ucsi_read_ack(ucsi, data, len); break; -- cgit v1.2.3 From 4aebc4f89f00a4cbfb0142c619e181f260e36546 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Fri, 26 May 2023 16:14:33 +0300 Subject: usb: typec: mux: Clean up mux_fwnode_match() Removing the "svid" and "accessory" device property checks. Those properties are not supported on any platform. Signed-off-by: Heikki Krogerus Link: https://lore.kernel.org/lkml/20230522215348.uoyboow26n2o3tel@ripper/ Reviewed-by: Bjorn Andersson Tested-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230526131434.46920-2-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux.c | 55 +++++++------------------------------------------ 1 file changed, 7 insertions(+), 48 deletions(-) diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index d9eaf9a0b0bf..f2eee6322815 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -265,60 +265,19 @@ static int mux_fwnode_match(struct device *dev, const void *fwnode) static void *typec_mux_match(const struct fwnode_handle *fwnode, const char *id, void *data) { - const struct typec_altmode_desc *desc = data; struct device *dev; - bool match; - int nval; - u16 *val; - int ret; - int i; /* - * Check has the identifier already been "consumed". If it - * has, no need to do any extra connection identification. + * Device graph (OF graph) does not give any means to identify the + * device type or the device class of the remote port parent that @fwnode + * represents, so in order to identify the type or the class of @fwnode + * an additional device property is needed. With typec muxes the + * property is named "mode-switch" (@id). The value of the device + * property is ignored. */ - match = !id; - if (match) - goto find_mux; - - if (!desc) { - /* - * Accessory Mode muxes & muxes which explicitly specify - * the required identifier can avoid SVID matching. - */ - match = fwnode_property_present(fwnode, "accessory") || - fwnode_property_present(fwnode, id); - if (match) - goto find_mux; - return NULL; - } - - /* Alternate Mode muxes */ - nval = fwnode_property_count_u16(fwnode, "svid"); - if (nval <= 0) + if (id && !fwnode_property_present(fwnode, id)) return NULL; - val = kcalloc(nval, sizeof(*val), GFP_KERNEL); - if (!val) - return ERR_PTR(-ENOMEM); - - ret = fwnode_property_read_u16_array(fwnode, "svid", val, nval); - if (ret < 0) { - kfree(val); - return ERR_PTR(ret); - } - - for (i = 0; i < nval; i++) { - match = val[i] == desc->svid; - if (match) { - kfree(val); - goto find_mux; - } - } - kfree(val); - return NULL; - -find_mux: dev = class_find_device(&typec_mux_class, NULL, fwnode, mux_fwnode_match); -- cgit v1.2.3 From 3524fe31538c1a1de1da2571b1f313f9469edf51 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Fri, 26 May 2023 16:14:34 +0300 Subject: usb: typec: mux: Remove alt mode parameters from the API The alt mode descriptor parameters are not used anymore. Signed-off-by: Heikki Krogerus Reviewed-by: Bjorn Andersson Tested-by: Bjorn Andersson Acked-by: Prashant Malani Link: https://lore.kernel.org/r/20230526131434.46920-3-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/platform/chrome/cros_ec_typec.c | 2 +- drivers/soc/qcom/pmic_glink_altmode.c | 5 +---- drivers/usb/typec/class.c | 4 ++-- drivers/usb/typec/mux.c | 6 ++---- include/linux/usb/typec_mux.h | 11 ++++------- 5 files changed, 10 insertions(+), 18 deletions(-) diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index a673c3342470..25f9767c28e8 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -77,7 +77,7 @@ static int cros_typec_get_switch_handles(struct cros_typec_port *port, { int ret = 0; - port->mux = fwnode_typec_mux_get(fwnode, NULL); + port->mux = fwnode_typec_mux_get(fwnode); if (IS_ERR(port->mux)) { ret = PTR_ERR(port->mux); dev_dbg(dev, "Mux handle not found: %d.\n", ret); diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c index 4d7895bdeaf2..df48fbea4b68 100644 --- a/drivers/soc/qcom/pmic_glink_altmode.c +++ b/drivers/soc/qcom/pmic_glink_altmode.c @@ -369,7 +369,6 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev, { struct pmic_glink_altmode_port *alt_port; struct pmic_glink_altmode *altmode; - struct typec_altmode_desc mux_desc = {}; const struct of_device_id *match; struct fwnode_handle *fwnode; struct device *dev = &adev->dev; @@ -427,9 +426,7 @@ static int pmic_glink_altmode_probe(struct auxiliary_device *adev, alt_port->dp_alt.mode = USB_TYPEC_DP_MODE; alt_port->dp_alt.active = 1; - mux_desc.svid = USB_TYPEC_DP_SID; - mux_desc.mode = USB_TYPEC_DP_MODE; - alt_port->typec_mux = fwnode_typec_mux_get(fwnode, &mux_desc); + alt_port->typec_mux = fwnode_typec_mux_get(fwnode); if (IS_ERR(alt_port->typec_mux)) return dev_err_probe(dev, PTR_ERR(alt_port->typec_mux), "failed to acquire mode-switch for port: %d\n", diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 349cc2030c90..faa184ae3dac 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -2110,7 +2110,7 @@ typec_port_register_altmode(struct typec_port *port, struct typec_mux *mux; struct typec_retimer *retimer; - mux = typec_mux_get(&port->dev, desc); + mux = typec_mux_get(&port->dev); if (IS_ERR(mux)) return ERR_CAST(mux); @@ -2274,7 +2274,7 @@ struct typec_port *typec_register_port(struct device *parent, return ERR_PTR(ret); } - port->mux = typec_mux_get(&port->dev, NULL); + port->mux = typec_mux_get(&port->dev); if (IS_ERR(port->mux)) { ret = PTR_ERR(port->mux); put_device(&port->dev); diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index f2eee6322815..80dd91938d96 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -287,15 +287,13 @@ static void *typec_mux_match(const struct fwnode_handle *fwnode, /** * fwnode_typec_mux_get - Find USB Type-C Multiplexer * @fwnode: The caller device node - * @desc: Alt Mode description * * Finds a mux linked to the caller. This function is primarily meant for the * Type-C drivers. Returns a reference to the mux on success, NULL if no * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection * was found but the mux has not been enumerated yet. */ -struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode, - const struct typec_altmode_desc *desc) +struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode) { struct typec_mux_dev *mux_devs[TYPEC_MUX_MAX_DEVS]; struct typec_mux *mux; @@ -308,7 +306,7 @@ struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode, return ERR_PTR(-ENOMEM); count = fwnode_connection_find_matches(fwnode, "mode-switch", - (void *)desc, typec_mux_match, + NULL, typec_mux_match, (void **)mux_devs, ARRAY_SIZE(mux_devs)); if (count <= 0) { diff --git a/include/linux/usb/typec_mux.h b/include/linux/usb/typec_mux.h index 9292f0e07846..11bfa314529f 100644 --- a/include/linux/usb/typec_mux.h +++ b/include/linux/usb/typec_mux.h @@ -60,8 +60,7 @@ struct typec_mux_desc { #if IS_ENABLED(CONFIG_TYPEC) -struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode, - const struct typec_altmode_desc *desc); +struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode); void typec_mux_put(struct typec_mux *mux); int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state); @@ -74,8 +73,7 @@ void *typec_mux_get_drvdata(struct typec_mux_dev *mux); #else -static inline struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode, - const struct typec_altmode_desc *desc) +static inline struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode); { return NULL; } @@ -102,10 +100,9 @@ static inline void *typec_mux_get_drvdata(struct typec_mux_dev *mux) #endif /* CONFIG_TYPEC */ -static inline struct typec_mux * -typec_mux_get(struct device *dev, const struct typec_altmode_desc *desc) +static inline struct typec_mux *typec_mux_get(struct device *dev) { - return fwnode_typec_mux_get(dev_fwnode(dev), desc); + return fwnode_typec_mux_get(dev_fwnode(dev)); } #endif /* __USB_TYPEC_MUX */ -- cgit v1.2.3 From 4c186faa7bb36d457939dd5bd4b8e6d5f9251cd6 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 15 May 2023 14:36:33 +0100 Subject: dt-bindings: regulator: qcom,usb-vbus-regulator: Mark reg as required The regulator code needs to know the location of the register to write to to switch on/off. Right now we have a driver that does this, a yaml that partially describes it and no dts that uses it. Switching on the VBUS for sm8250 shows that we haven't documented reg as a required property, do so now. Acked-by: Krzysztof Kozlowski Signed-off-by: Bryan O'Donoghue Link: https://lore.kernel.org/r/20230515133643.3621656-2-bryan.odonoghue@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/regulator/qcom,usb-vbus-regulator.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/regulator/qcom,usb-vbus-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,usb-vbus-regulator.yaml index b1cff3adb21b..7a3b59f83609 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,usb-vbus-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom,usb-vbus-regulator.yaml @@ -25,6 +25,7 @@ properties: required: - compatible + - reg additionalProperties: false -- cgit v1.2.3 From 581d79f7deedac1f45edbbf1e185f5e601565426 Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 15 May 2023 14:36:34 +0100 Subject: dt-bindings: regulator: qcom,usb-vbus-regulator: Mark regulator-*-microamp required The VBUS driver needs to know the regulator-min-microamp and regulator-max-microamp so they should both be marked as required. regulator.yaml defines those two dependencies so include regulator.yaml. We need to change from additionalProperties: false to unevaluatedProperties: false. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bryan O'Donoghue Link: https://lore.kernel.org/r/20230515133643.3621656-3-bryan.odonoghue@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/regulator/qcom,usb-vbus-regulator.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/regulator/qcom,usb-vbus-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,usb-vbus-regulator.yaml index 7a3b59f83609..89c564dfa5db 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,usb-vbus-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom,usb-vbus-regulator.yaml @@ -14,6 +14,9 @@ description: | regulator will be enabled in situations where the device is required to provide power to the connected peripheral. +allOf: + - $ref: regulator.yaml# + properties: compatible: enum: @@ -26,8 +29,10 @@ properties: required: - compatible - reg + - regulator-min-microamp + - regulator-max-microamp -additionalProperties: false +unevaluatedProperties: false examples: - | @@ -37,6 +42,8 @@ examples: pm8150b_vbus: usb-vbus-regulator@1100 { compatible = "qcom,pm8150b-vbus-reg"; reg = <0x1100>; + regulator-min-microamp = <500000>; + regulator-max-microamp = <3000000>; }; }; ... -- cgit v1.2.3 From 24520e51fffb56f3931084b1426cb2404bda095a Mon Sep 17 00:00:00 2001 From: Bryan O'Donoghue Date: Mon, 15 May 2023 14:36:35 +0100 Subject: dt-bindings: phy: qcom,sc7180-qmp-usb3-dp-phy: Add orientation-switch as optional orientation-switch it the standard declaration to inform the Type-C mux layer that a remote-endpoint is capable of processing orientation change messages. Add as an optional since not all versions of the dp-phy currently support the orientation-switch. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bryan O'Donoghue Link: https://lore.kernel.org/r/20230515133643.3621656-4-bryan.odonoghue@linaro.org Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/phy/qcom,sc7180-qmp-usb3-dp-phy.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,sc7180-qmp-usb3-dp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,sc7180-qmp-usb3-dp-phy.yaml index 0ef2c9b9d466..d30734338888 100644 --- a/Documentation/devicetree/bindings/phy/qcom,sc7180-qmp-usb3-dp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,sc7180-qmp-usb3-dp-phy.yaml @@ -61,6 +61,10 @@ properties: power-domains: maxItems: 1 + orientation-switch: + description: Flag the port as possible handler of orientation switching + type: boolean + resets: items: - description: reset of phy block. @@ -251,6 +255,8 @@ examples: vdda-phy-supply = <&vdda_usb2_ss_1p2>; vdda-pll-supply = <&vdda_usb2_ss_core>; + orientation-switch; + usb3-phy@200 { reg = <0x200 0x128>, <0x400 0x200>, -- cgit v1.2.3 From 2f6ecb89fe8feb2b60a53325b0eeb9866d88909a Mon Sep 17 00:00:00 2001 From: Prashanth K Date: Fri, 5 May 2023 14:48:37 +0530 Subject: usb: gadget: u_serial: Add null pointer check in gserial_suspend Consider a case where gserial_disconnect has already cleared gser->ioport. And if gserial_suspend gets called afterwards, it will lead to accessing of gser->ioport and thus causing null pointer dereference. Avoid this by adding a null pointer check. Added a static spinlock to prevent gser->ioport from becoming null after the newly added null pointer check. Fixes: aba3a8d01d62 ("usb: gadget: u_serial: add suspend resume callbacks") Signed-off-by: Prashanth K Link: https://lore.kernel.org/r/1683278317-11774-1-git-send-email-quic_prashk@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/u_serial.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index a0ca47fbff0f..e5d522d54f6a 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -1420,10 +1420,19 @@ EXPORT_SYMBOL_GPL(gserial_disconnect); void gserial_suspend(struct gserial *gser) { - struct gs_port *port = gser->ioport; + struct gs_port *port; unsigned long flags; - spin_lock_irqsave(&port->port_lock, flags); + spin_lock_irqsave(&serial_port_lock, flags); + port = gser->ioport; + + if (!port) { + spin_unlock_irqrestore(&serial_port_lock, flags); + return; + } + + spin_lock(&port->port_lock); + spin_unlock(&serial_port_lock); port->suspended = true; spin_unlock_irqrestore(&port->port_lock, flags); } -- cgit v1.2.3 From e5990469943c711cb00bfde6338d2add6c6d0bfe Mon Sep 17 00:00:00 2001 From: Prashanth K Date: Tue, 9 May 2023 18:57:52 +0530 Subject: usb: gadget: u_serial: Avoid spinlock recursion in __gs_console_push When serial console over USB is enabled, gs_console_connect queues gs_console_work, where it acquires the spinlock and queues the usb request, and this request goes to gadget layer. Now consider a situation where gadget layer prints something to dmesg, this will eventually call gs_console_write() which requires cons->lock. And this causes spinlock recursion. Avoid this by excluding usb_ep_queue from the spinlock. spin_lock_irqsave //needs cons->lock gs_console_write . . _printk __warn_printk dev_warn/pr_err . . [USB Gadget Layer] . . usb_ep_queue gs_console_work __gs_console_push // acquires cons->lock process_one_work Signed-off-by: Prashanth K Link: https://lore.kernel.org/r/1683638872-6885-1-git-send-email-quic_prashk@quicinc.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/u_serial.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index e5d522d54f6a..97f07757d19e 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -916,8 +916,11 @@ static void __gs_console_push(struct gs_console *cons) } req->length = size; + + spin_unlock_irq(&cons->lock); if (usb_ep_queue(ep, req, GFP_ATOMIC)) req->length = 0; + spin_lock_irq(&cons->lock); } static void gs_console_work(struct work_struct *work) -- cgit v1.2.3 From c3ff12a92bd7072170978b8b41c2fa41b038139a Mon Sep 17 00:00:00 2001 From: Avichal Rakesh Date: Mon, 8 May 2023 16:11:03 -0700 Subject: usb: gadget: uvc: queue empty isoc requests if no video buffer is available ISOC transfers expect a certain cadence of requests being queued. Not keeping up with the expected rate of requests results in missed ISOC transfers (EXDEV). The application layer may or may not produce video frames to match this expectation, so uvc gadget driver must handle cases where the application is not queuing up buffers fast enough to fulfill ISOC requirements. Currently, uvc gadget driver waits for new video buffer to become available before queuing up usb requests. With this patch the gadget driver queues up 0 length usb requests whenever there are no video buffers available. The USB controller's complete callback is used as the limiter for how quickly the 0 length packets will be queued. Video buffers are still queued as soon as they become available. Link: https://lore.kernel.org/CAMHf4WKbi6KBPQztj9FA4kPvESc1fVKrC8G73-cs6tTeQby9=w@mail.gmail.com/ Signed-off-by: Avichal Rakesh Link: https://lore.kernel.org/r/20230508231103.1621375-1-arakesh@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/uvc_video.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index dd1c6b2ca7c6..e81865978299 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -386,6 +386,9 @@ static void uvcg_video_pump(struct work_struct *work) struct uvc_buffer *buf; unsigned long flags; int ret; + bool buf_int; + /* video->max_payload_size is only set when using bulk transfer */ + bool is_bulk = video->max_payload_size; while (video->ep->enabled) { /* @@ -408,20 +411,35 @@ static void uvcg_video_pump(struct work_struct *work) */ spin_lock_irqsave(&queue->irqlock, flags); buf = uvcg_queue_head(queue); - if (buf == NULL) { + + if (buf != NULL) { + video->encode(req, video, buf); + /* Always interrupt for the last request of a video buffer */ + buf_int = buf->state == UVC_BUF_STATE_DONE; + } else if (!(queue->flags & UVC_QUEUE_DISCONNECTED) && !is_bulk) { + /* + * No video buffer available; the queue is still connected and + * we're traferring over ISOC. Queue a 0 length request to + * prevent missed ISOC transfers. + */ + req->length = 0; + buf_int = false; + } else { + /* + * Either queue has been disconnected or no video buffer + * available to bulk transfer. Either way, stop processing + * further. + */ spin_unlock_irqrestore(&queue->irqlock, flags); break; } - video->encode(req, video, buf); - /* * With usb3 we have more requests. This will decrease the * interrupt load to a quarter but also catches the corner * cases, which needs to be handled. */ - if (list_empty(&video->req_free) || - buf->state == UVC_BUF_STATE_DONE || + if (list_empty(&video->req_free) || buf_int || !(video->req_int_count % DIV_ROUND_UP(video->uvc_num_requests, 4))) { video->req_int_count = 0; @@ -441,8 +459,7 @@ static void uvcg_video_pump(struct work_struct *work) /* Endpoint now owns the request */ req = NULL; - if (buf->state != UVC_BUF_STATE_DONE) - video->req_int_count++; + video->req_int_count++; } if (!req) @@ -527,4 +544,3 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc) V4L2_BUF_TYPE_VIDEO_OUTPUT, &video->mutex); return 0; } - -- cgit v1.2.3 From be3d5a493b666a8faed8c08ece0fce7a9c9cc741 Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Mon, 22 May 2023 12:50:41 +0200 Subject: usb: add HAS_IOPORT dependencies In a future patch HAS_IOPORT=n will result in inb()/outb() and friends not being declared. We thus need to add HAS_IOPORT as dependency for those drivers using them. Co-developed-by: Arnd Bergmann Signed-off-by: Arnd Bergmann Signed-off-by: Niklas Schnelle Link: https://lore.kernel.org/r/20230522105049.1467313-37-schnelle@linux.ibm.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index c170672f847e..4448d0ab06f0 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -376,7 +376,7 @@ config USB_ISP116X_HCD config USB_ISP1362_HCD tristate "ISP1362 HCD support" - depends on HAS_IOMEM + depends on HAS_IOPORT depends on COMPILE_TEST # nothing uses this help Supports the Philips ISP1362 chip as a host controller @@ -578,7 +578,7 @@ endif # USB_OHCI_HCD config USB_UHCI_HCD tristate "UHCI HCD (most Intel and VIA) support" - depends on USB_PCI || USB_UHCI_SUPPORT_NON_PCI_HC + depends on (USB_PCI && HAS_IOPORT) || USB_UHCI_SUPPORT_NON_PCI_HC help The Universal Host Controller Interface is a standard by Intel for accessing the USB hardware in the PC (which is also called the USB -- cgit v1.2.3 From dc54ce3e603b0cde997465a4511fae41c0447bab Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Mon, 22 May 2023 12:50:42 +0200 Subject: usb: uhci: handle HAS_IOPORT dependencies In a future patch HAS_IOPORT=n will result in inb()/outb() and friends not being declared. We thus need to guard sections of code calling them as alternative access methods with CONFIG_HAS_IOPORT checks. For uhci-hcd there are a lot of I/O port uses that do have MMIO alternatives all selected by uhci_has_pci_registers() so this can be handled by UHCI_IN/OUT macros and making uhci_has_pci_registers() constant 0 if CONFIG_HAS_IOPORT is unset. Co-developed-by: Arnd Bergmann Signed-off-by: Arnd Bergmann Signed-off-by: Niklas Schnelle Reviewed-by: Alan Stern Link: https://lore.kernel.org/r/20230522105049.1467313-38-schnelle@linux.ibm.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.c | 2 +- drivers/usb/host/uhci-hcd.h | 24 +++++++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 7cdc2fa7c28f..fd2408b553cf 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -841,7 +841,7 @@ static int uhci_count_ports(struct usb_hcd *hcd) static const char hcd_name[] = "uhci_hcd"; -#ifdef CONFIG_USB_PCI +#if defined(CONFIG_USB_PCI) && defined(CONFIG_HAS_IOPORT) #include "uhci-pci.c" #define PCI_DRIVER uhci_pci_driver #endif diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 0688c3e5bfe2..13ee2a6144b2 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -505,6 +505,14 @@ static inline bool uhci_is_aspeed(const struct uhci_hcd *uhci) * we use memory mapped registers. */ +#ifdef CONFIG_HAS_IOPORT +#define UHCI_IN(x) x +#define UHCI_OUT(x) x +#else +#define UHCI_IN(x) 0 +#define UHCI_OUT(x) do { } while (0) +#endif + #ifndef CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC /* Support PCI only */ static inline u32 uhci_readl(const struct uhci_hcd *uhci, int reg) @@ -539,7 +547,7 @@ static inline void uhci_writeb(const struct uhci_hcd *uhci, u8 val, int reg) #else /* Support non-PCI host controllers */ -#ifdef CONFIG_USB_PCI +#if defined(CONFIG_USB_PCI) && defined(HAS_IOPORT) /* Support PCI and non-PCI host controllers */ #define uhci_has_pci_registers(u) ((u)->io_addr != 0) #else @@ -587,7 +595,7 @@ static inline int uhci_aspeed_reg(unsigned int reg) static inline u32 uhci_readl(const struct uhci_hcd *uhci, int reg) { if (uhci_has_pci_registers(uhci)) - return inl(uhci->io_addr + reg); + return UHCI_IN(inl(uhci->io_addr + reg)); else if (uhci_is_aspeed(uhci)) return readl(uhci->regs + uhci_aspeed_reg(reg)); #ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO @@ -601,7 +609,7 @@ static inline u32 uhci_readl(const struct uhci_hcd *uhci, int reg) static inline void uhci_writel(const struct uhci_hcd *uhci, u32 val, int reg) { if (uhci_has_pci_registers(uhci)) - outl(val, uhci->io_addr + reg); + UHCI_OUT(outl(val, uhci->io_addr + reg)); else if (uhci_is_aspeed(uhci)) writel(val, uhci->regs + uhci_aspeed_reg(reg)); #ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO @@ -615,7 +623,7 @@ static inline void uhci_writel(const struct uhci_hcd *uhci, u32 val, int reg) static inline u16 uhci_readw(const struct uhci_hcd *uhci, int reg) { if (uhci_has_pci_registers(uhci)) - return inw(uhci->io_addr + reg); + return UHCI_IN(inw(uhci->io_addr + reg)); else if (uhci_is_aspeed(uhci)) return readl(uhci->regs + uhci_aspeed_reg(reg)); #ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO @@ -629,7 +637,7 @@ static inline u16 uhci_readw(const struct uhci_hcd *uhci, int reg) static inline void uhci_writew(const struct uhci_hcd *uhci, u16 val, int reg) { if (uhci_has_pci_registers(uhci)) - outw(val, uhci->io_addr + reg); + UHCI_OUT(outw(val, uhci->io_addr + reg)); else if (uhci_is_aspeed(uhci)) writel(val, uhci->regs + uhci_aspeed_reg(reg)); #ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO @@ -643,7 +651,7 @@ static inline void uhci_writew(const struct uhci_hcd *uhci, u16 val, int reg) static inline u8 uhci_readb(const struct uhci_hcd *uhci, int reg) { if (uhci_has_pci_registers(uhci)) - return inb(uhci->io_addr + reg); + return UHCI_IN(inb(uhci->io_addr + reg)); else if (uhci_is_aspeed(uhci)) return readl(uhci->regs + uhci_aspeed_reg(reg)); #ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO @@ -657,7 +665,7 @@ static inline u8 uhci_readb(const struct uhci_hcd *uhci, int reg) static inline void uhci_writeb(const struct uhci_hcd *uhci, u8 val, int reg) { if (uhci_has_pci_registers(uhci)) - outb(val, uhci->io_addr + reg); + UHCI_OUT(outb(val, uhci->io_addr + reg)); else if (uhci_is_aspeed(uhci)) writel(val, uhci->regs + uhci_aspeed_reg(reg)); #ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO @@ -668,6 +676,8 @@ static inline void uhci_writeb(const struct uhci_hcd *uhci, u8 val, int reg) writeb(val, uhci->regs + reg); } #endif /* CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC */ +#undef UHCI_IN +#undef UHCI_OUT /* * The GRLIB GRUSBHC controller can use big endian format for its descriptors. -- cgit v1.2.3 From 4a680fcd60d4476236e1b8e34585d480aeda677f Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 16 May 2023 10:34:32 +0200 Subject: dt-bindings: usb: usb251xb: correct swap-dx-lanes type to uint32 The "swap-dx-lanes" was never described as uint8 in original TXT bindings and Linux driver expects uint32. Fix the type to match Linux driver expectation. Fixes: fff61d4ccf3d ("dt-bindings: usb: usb251xb: Convert to YAML schema") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Marek Vasut Acked-by: Conor Dooley Reviewed-by: Richard Leitner Link: https://lore.kernel.org/r/20230516083432.18579-1-krzysztof.kozlowski@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/usb251xb.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/usb251xb.yaml b/Documentation/devicetree/bindings/usb/usb251xb.yaml index 4d1530816817..ac5b99710332 100644 --- a/Documentation/devicetree/bindings/usb/usb251xb.yaml +++ b/Documentation/devicetree/bindings/usb/usb251xb.yaml @@ -231,7 +231,7 @@ properties: power-on sequence to a port until the port has adequate power. swap-dx-lanes: - $ref: /schemas/types.yaml#/definitions/uint8-array + $ref: /schemas/types.yaml#/definitions/uint32-array description: | Specifies the ports which will swap the differential-pair (D+/D-), default is not-swapped. -- cgit v1.2.3 From 342161c11403ea00e9febc16baab1d883d589d04 Mon Sep 17 00:00:00 2001 From: Li Yang Date: Thu, 20 Apr 2023 22:08:31 +0800 Subject: usb: phy: phy-tahvo: fix memory leak in tahvo_usb_probe() Smatch reports: drivers/usb/phy/phy-tahvo.c: tahvo_usb_probe() warn: missing unwind goto? After geting irq, if ret < 0, it will return without error handling to free memory. Just add error handling to fix this problem. Fixes: 0d45a1373e66 ("usb: phy: tahvo: add IRQ check") Signed-off-by: Li Yang Reviewed-by: Dongliang Mu Link: https://lore.kernel.org/r/20230420140832.9110-1-lidaxian@hust.edu.cn Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tahvo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c index 47562d49dfc1..5cac31c6029b 100644 --- a/drivers/usb/phy/phy-tahvo.c +++ b/drivers/usb/phy/phy-tahvo.c @@ -391,7 +391,7 @@ static int tahvo_usb_probe(struct platform_device *pdev) tu->irq = ret = platform_get_irq(pdev, 0); if (ret < 0) - return ret; + goto err_remove_phy; ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt, IRQF_ONESHOT, "tahvo-vbus", tu); -- cgit v1.2.3 From 8e6bd945e6dde64fbc60ec3fe252164493a8d3a2 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 16 May 2023 22:17:42 +0200 Subject: usb: hide unused usbfs_notify_suspend/resume functions The declaration is in an #ifdef, which causes warnings when building with 'make W=1' and without CONFIG_PM: drivers/usb/core/devio.c:742:6: error: no previous prototype for 'usbfs_notify_suspend' drivers/usb/core/devio.c:747:6: error: no previous prototype for 'usbfs_notify_resume' Use the same #ifdef check around the function definitions to avoid the warnings and slightly shrink the USB core. Fixes: 7794f486ed0b ("usbfs: Add ioctls for runtime power management") Signed-off-by: Arnd Bergmann Reviewed-by: Sebastian Reichel Acked-by: Alan Stern Link: https://lore.kernel.org/r/20230516202103.558301-1-arnd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index e501a03d6c70..1622ad35428d 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -738,6 +738,7 @@ static int driver_resume(struct usb_interface *intf) return 0; } +#ifdef CONFIG_PM /* The following routines apply to the entire device, not interfaces */ void usbfs_notify_suspend(struct usb_device *udev) { @@ -756,6 +757,7 @@ void usbfs_notify_resume(struct usb_device *udev) } mutex_unlock(&usbfs_mutex); } +#endif struct usb_driver usbfs_driver = { .name = "usbfs", -- cgit v1.2.3 From 6f5bd24f50feef22923af431826e1f74f3ffbf00 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Wed, 17 May 2023 16:19:05 +0800 Subject: usb: chipidea: imx: remove one duplicated reg define Remove one duplicated definition of MX7D_USB_OTG_PHY_CFG1. Signed-off-by: Li Jun Signed-off-by: Xu Yang Link: https://lore.kernel.org/r/20230517081907.3410465-1-xu.yang_2@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/usbmisc_imx.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index c57c1a71a513..be939e77719d 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -113,7 +113,6 @@ #define MX7D_USBNC_USB_CTRL2_DP_DM_MASK (BIT(12) | BIT(13) | \ BIT(14) | BIT(15)) -#define MX7D_USB_OTG_PHY_CFG1 0x30 #define MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL BIT(0) #define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 BIT(1) #define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 BIT(2) -- cgit v1.2.3 From 7a053bf2b36622996356018a7a224c20d0ddcea9 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 17 May 2023 11:25:44 -0400 Subject: usb: cdns3: imx: simplify clock name usage Simplifies the clock names in imx_cdns3_core_clks[]. Such as, renaming "usb3_lpm_clk" to "lpm". The "usb3" prefix and "clk" suffix were redundant. Signed-off-by: Frank Li Link: https://lore.kernel.org/r/20230517152545.3404508-1-Frank.Li@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-imx.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c index e5930f894315..31c39728156f 100644 --- a/drivers/usb/cdns3/cdns3-imx.c +++ b/drivers/usb/cdns3/cdns3-imx.c @@ -105,11 +105,11 @@ static inline void cdns_imx_writel(struct cdns_imx *data, u32 offset, u32 value) } static const struct clk_bulk_data imx_cdns3_core_clks[] = { - { .id = "usb3_lpm_clk" }, - { .id = "usb3_bus_clk" }, - { .id = "usb3_aclk" }, - { .id = "usb3_ipg_clk" }, - { .id = "usb3_core_pclk" }, + { .id = "lpm" }, + { .id = "bus" }, + { .id = "aclk" }, + { .id = "ipg" }, + { .id = "core" }, }; static int cdns_imx_noncore_init(struct cdns_imx *data) -- cgit v1.2.3 From 8486eb8068ccfea99864544327a7d18f8a1e231e Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 17 May 2023 11:25:45 -0400 Subject: dt-bindings: usb: cdns-imx8qm: add imx8qm cdns3 glue layer NXP imx8qm integrates 1 cdns3 IP. This is glue layer device bindings. Signed-off-by: Frank Li Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230517152545.3404508-2-Frank.Li@nxp.com Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/fsl,imx8qm-cdns3.yaml | 103 +++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/fsl,imx8qm-cdns3.yaml diff --git a/Documentation/devicetree/bindings/usb/fsl,imx8qm-cdns3.yaml b/Documentation/devicetree/bindings/usb/fsl,imx8qm-cdns3.yaml new file mode 100644 index 000000000000..ceb76394af60 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/fsl,imx8qm-cdns3.yaml @@ -0,0 +1,103 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (c) 2020 NXP +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/fsl,imx8qm-cdns3.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP iMX8QM Soc USB Controller + +maintainers: + - Frank Li + +properties: + compatible: + const: fsl,imx8qm-usb3 + + reg: + items: + - description: Register set for iMX USB3 Platform Control + + "#address-cells": + enum: [ 1, 2 ] + + "#size-cells": + enum: [ 1, 2 ] + + ranges: true + + clocks: + items: + - description: Standby clock. Used during ultra low power states. + - description: USB bus clock for usb3 controller. + - description: AXI clock for AXI interface. + - description: ipg clock for register access. + - description: Core clock for usb3 controller. + + clock-names: + items: + - const: lpm + - const: bus + - const: aclk + - const: ipg + - const: core + + power-domains: + maxItems: 1 + +# Required child node: + +patternProperties: + "^usb@[0-9a-f]+$": + $ref: cdns,usb3.yaml# + +required: + - compatible + - reg + - "#address-cells" + - "#size-cells" + - ranges + - clocks + - clock-names + - power-domains + +additionalProperties: false + +examples: + - | + #include + #include + #include + + usb@5b110000 { + compatible = "fsl,imx8qm-usb3"; + reg = <0x5b110000 0x10000>; + ranges; + clocks = <&usb3_lpcg IMX_LPCG_CLK_1>, + <&usb3_lpcg IMX_LPCG_CLK_0>, + <&usb3_lpcg IMX_LPCG_CLK_7>, + <&usb3_lpcg IMX_LPCG_CLK_4>, + <&usb3_lpcg IMX_LPCG_CLK_5>; + clock-names = "lpm", "bus", "aclk", "ipg", "core"; + assigned-clocks = <&clk IMX_SC_R_USB_2 IMX_SC_PM_CLK_MST_BUS>; + assigned-clock-rates = <250000000>; + power-domains = <&pd IMX_SC_R_USB_2>; + #address-cells = <1>; + #size-cells = <1>; + + usb@5b120000 { + compatible = "cdns,usb3"; + reg = <0x5b120000 0x10000>, /* memory area for OTG/DRD registers */ + <0x5b130000 0x10000>, /* memory area for HOST registers */ + <0x5b140000 0x10000>; /* memory area for DEVICE registers */ + reg-names = "otg", "xhci", "dev"; + interrupt-parent = <&gic>; + interrupts = , + , + , + ; + interrupt-names = "host", "peripheral", "otg", "wakeup"; + phys = <&usb3_phy>; + phy-names = "cdns3,usb3-phy"; + }; + }; -- cgit v1.2.3 From bfb46b424652a3396b92ca3c96c169ade9b45b8d Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 18 May 2023 19:27:49 +0800 Subject: usb: cdns3: Add StarFive JH7110 USB driver Adds Specific Glue layer to support USB peripherals on StarFive JH7110 SoC. There is a Cadence USB3 core for JH7110 SoCs, the cdns core is the child of this USB wrapper module device. Signed-off-by: Minda Chen Acked-by: Peter Chen Reviewed-by: Roger Quadros Link: https://lore.kernel.org/r/20230518112750.57924-7-minda.chen@starfivetech.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 6 + drivers/usb/cdns3/Kconfig | 11 ++ drivers/usb/cdns3/Makefile | 1 + drivers/usb/cdns3/cdns3-starfive.c | 246 +++++++++++++++++++++++++++++++++++++ 4 files changed, 264 insertions(+) create mode 100644 drivers/usb/cdns3/cdns3-starfive.c diff --git a/MAINTAINERS b/MAINTAINERS index ec9fea153b6b..d9ce3f79b8df 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20137,6 +20137,12 @@ F: Documentation/devicetree/bindings/reset/starfive,jh7100-reset.yaml F: drivers/reset/starfive/reset-starfive-jh71* F: include/dt-bindings/reset/starfive?jh71*.h +STARFIVE JH71X0 USB DRIVERS +M: Minda Chen +S: Maintained +F: Documentation/devicetree/bindings/usb/starfive,jh7110-usb.yaml +F: drivers/usb/cdns3/cdns3-starfive.c + STARFIVE JH71XX PMU CONTROLLER DRIVER M: Walker Chen S: Supported diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig index b98ca0a1352a..0a514b591527 100644 --- a/drivers/usb/cdns3/Kconfig +++ b/drivers/usb/cdns3/Kconfig @@ -78,6 +78,17 @@ config USB_CDNS3_IMX For example, imx8qm and imx8qxp. +config USB_CDNS3_STARFIVE + tristate "Cadence USB3 support on StarFive SoC platforms" + depends on ARCH_STARFIVE || COMPILE_TEST + help + Say 'Y' or 'M' here if you are building for StarFive SoCs + platforms that contain Cadence USB3 controller core. + + e.g. JH7110. + + If you choose to build this driver as module it will + be dynamically linked and module will be called cdns3-starfive.ko endif if USB_CDNS_SUPPORT diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile index 61edb2f89276..48dfae75b5aa 100644 --- a/drivers/usb/cdns3/Makefile +++ b/drivers/usb/cdns3/Makefile @@ -24,6 +24,7 @@ endif obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o obj-$(CONFIG_USB_CDNS3_TI) += cdns3-ti.o obj-$(CONFIG_USB_CDNS3_IMX) += cdns3-imx.o +obj-$(CONFIG_USB_CDNS3_STARFIVE) += cdns3-starfive.o cdnsp-udc-pci-y := cdnsp-pci.o diff --git a/drivers/usb/cdns3/cdns3-starfive.c b/drivers/usb/cdns3/cdns3-starfive.c new file mode 100644 index 000000000000..fc1f003b145d --- /dev/null +++ b/drivers/usb/cdns3/cdns3-starfive.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0 +/** + * cdns3-starfive.c - StarFive specific Glue layer for Cadence USB Controller + * + * Copyright (C) 2023 StarFive Technology Co., Ltd. + * + * Author: Minda Chen + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" + +#define USB_STRAP_HOST BIT(17) +#define USB_STRAP_DEVICE BIT(18) +#define USB_STRAP_MASK GENMASK(18, 16) + +#define USB_SUSPENDM_HOST BIT(19) +#define USB_SUSPENDM_MASK BIT(19) + +#define USB_MISC_CFG_MASK GENMASK(23, 20) +#define USB_SUSPENDM_BYPS BIT(20) +#define USB_PLL_EN BIT(22) +#define USB_REFCLK_MODE BIT(23) + +struct cdns_starfive { + struct device *dev; + struct regmap *stg_syscon; + struct reset_control *resets; + struct clk_bulk_data *clks; + int num_clks; + u32 stg_usb_mode; +}; + +static void cdns_mode_init(struct platform_device *pdev, + struct cdns_starfive *data) +{ + enum usb_dr_mode mode; + + regmap_update_bits(data->stg_syscon, data->stg_usb_mode, + USB_MISC_CFG_MASK, + USB_SUSPENDM_BYPS | USB_PLL_EN | USB_REFCLK_MODE); + + /* dr mode setting */ + mode = usb_get_dr_mode(&pdev->dev); + + switch (mode) { + case USB_DR_MODE_HOST: + regmap_update_bits(data->stg_syscon, + data->stg_usb_mode, + USB_STRAP_MASK, + USB_STRAP_HOST); + regmap_update_bits(data->stg_syscon, + data->stg_usb_mode, + USB_SUSPENDM_MASK, + USB_SUSPENDM_HOST); + break; + + case USB_DR_MODE_PERIPHERAL: + regmap_update_bits(data->stg_syscon, data->stg_usb_mode, + USB_STRAP_MASK, USB_STRAP_DEVICE); + regmap_update_bits(data->stg_syscon, data->stg_usb_mode, + USB_SUSPENDM_MASK, 0); + break; + default: + break; + } +} + +static int cdns_clk_rst_init(struct cdns_starfive *data) +{ + int ret; + + ret = clk_bulk_prepare_enable(data->num_clks, data->clks); + if (ret) + return dev_err_probe(data->dev, ret, + "failed to enable clocks\n"); + + ret = reset_control_deassert(data->resets); + if (ret) { + dev_err(data->dev, "failed to reset clocks\n"); + goto err_clk_init; + } + + return ret; + +err_clk_init: + clk_bulk_disable_unprepare(data->num_clks, data->clks); + return ret; +} + +static void cdns_clk_rst_deinit(struct cdns_starfive *data) +{ + reset_control_assert(data->resets); + clk_bulk_disable_unprepare(data->num_clks, data->clks); +} + +static int cdns_starfive_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cdns_starfive *data; + unsigned int args; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = dev; + + data->stg_syscon = + syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node, + "starfive,stg-syscon", 1, &args); + + if (IS_ERR(data->stg_syscon)) + return dev_err_probe(dev, PTR_ERR(data->stg_syscon), + "Failed to parse starfive,stg-syscon\n"); + + data->stg_usb_mode = args; + + data->num_clks = devm_clk_bulk_get_all(data->dev, &data->clks); + if (data->num_clks < 0) + return dev_err_probe(data->dev, -ENODEV, + "Failed to get clocks\n"); + + data->resets = devm_reset_control_array_get_exclusive(data->dev); + if (IS_ERR(data->resets)) + return dev_err_probe(data->dev, PTR_ERR(data->resets), + "Failed to get resets"); + + cdns_mode_init(pdev, data); + ret = cdns_clk_rst_init(data); + if (ret) + return ret; + + ret = of_platform_populate(dev->of_node, NULL, NULL, dev); + if (ret) { + dev_err(dev, "Failed to create children\n"); + cdns_clk_rst_deinit(data); + return ret; + } + + device_set_wakeup_capable(dev, true); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + platform_set_drvdata(pdev, data); + + return 0; +} + +static int cdns_starfive_remove_core(struct device *dev, void *c) +{ + struct platform_device *pdev = to_platform_device(dev); + + platform_device_unregister(pdev); + + return 0; +} + +static int cdns_starfive_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cdns_starfive *data = dev_get_drvdata(dev); + + pm_runtime_get_sync(dev); + device_for_each_child(dev, NULL, cdns_starfive_remove_core); + + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); + cdns_clk_rst_deinit(data); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM +static int cdns_starfive_runtime_resume(struct device *dev) +{ + struct cdns_starfive *data = dev_get_drvdata(dev); + + return clk_bulk_prepare_enable(data->num_clks, data->clks); +} + +static int cdns_starfive_runtime_suspend(struct device *dev) +{ + struct cdns_starfive *data = dev_get_drvdata(dev); + + clk_bulk_disable_unprepare(data->num_clks, data->clks); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cdns_starfive_resume(struct device *dev) +{ + struct cdns_starfive *data = dev_get_drvdata(dev); + + return cdns_clk_rst_init(data); +} + +static int cdns_starfive_suspend(struct device *dev) +{ + struct cdns_starfive *data = dev_get_drvdata(dev); + + cdns_clk_rst_deinit(data); + + return 0; +} +#endif +#endif + +static const struct dev_pm_ops cdns_starfive_pm_ops = { + SET_RUNTIME_PM_OPS(cdns_starfive_runtime_suspend, + cdns_starfive_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(cdns_starfive_suspend, cdns_starfive_resume) +}; + +static const struct of_device_id cdns_starfive_of_match[] = { + { .compatible = "starfive,jh7110-usb", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, cdns_starfive_of_match); + +static struct platform_driver cdns_starfive_driver = { + .probe = cdns_starfive_probe, + .remove = cdns_starfive_remove, + .driver = { + .name = "cdns3-starfive", + .of_match_table = cdns_starfive_of_match, + .pm = &cdns_starfive_pm_ops, + }, +}; +module_platform_driver(cdns_starfive_driver); + +MODULE_ALIAS("platform:cdns3-starfive"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Cadence USB3 StarFive Glue Layer"); -- cgit v1.2.3 From 2a1c4639d6d6bcee27f74e38f83ffb43579c4733 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Thu, 18 May 2023 16:49:45 -0400 Subject: usb: cdns3: improve handling of unaligned address case When the address of a request was not aligned with an 8-byte boundary, the USB DMA was unable to process it, necessitating the use of an internal bounce buffer. In these cases, the request->buf had to be copied to/from this bounce buffer. However, if this unaligned address scenario arises, it is unnecessary to perform heavy cache maintenance operations like usb_gadget_map(unmap)_request_by_dev() on the request->buf, as the DMA does not utilize it at all. it can be skipped at this case. iperf3 tests on the rndis case: Transmit speed (TX): Improved from 299Mbps to 440Mbps Receive speed (RX): Improved from 290Mbps to 500Mbps Signed-off-by: Frank Li Link: https://lore.kernel.org/r/20230518204947.3770236-1-Frank.Li@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-gadget.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index ccfaebca6faa..d2cedd6db88f 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -800,7 +800,8 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep, if (request->status == -EINPROGRESS) request->status = status; - usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request, + if (likely(!(priv_req->flags & REQUEST_UNALIGNED))) + usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request, priv_ep->dir); if ((priv_req->flags & REQUEST_UNALIGNED) && @@ -2530,10 +2531,12 @@ static int __cdns3_gadget_ep_queue(struct usb_ep *ep, if (ret < 0) return ret; - ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request, + if (likely(!(priv_req->flags & REQUEST_UNALIGNED))) { + ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request, usb_endpoint_dir_in(ep->desc)); - if (ret) - return ret; + if (ret) + return ret; + } list_add_tail(&request->list, &priv_ep->deferred_req_list); -- cgit v1.2.3 From 3124387537bc94251e65c2841062d14736380ec4 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Thu, 18 May 2023 16:49:46 -0400 Subject: usb: cdns3: optimize OUT transfer by copying only actual received data Previously, the entire length of the request, which is equal to or greater than the actual data, was dma synced and memcpy when using the bounce buffer. Actually only the actual data indicated by request->actual need be synced and copied. Signed-off-by: Frank Li Link: https://lore.kernel.org/r/20230518204947.3770236-2-Frank.Li@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-gadget.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index d2cedd6db88f..27199198cc26 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -809,10 +809,10 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep, /* Make DMA buffer CPU accessible */ dma_sync_single_for_cpu(priv_dev->sysdev, priv_req->aligned_buf->dma, - priv_req->aligned_buf->size, + request->actual, priv_req->aligned_buf->dir); memcpy(request->buf, priv_req->aligned_buf->buf, - request->length); + request->actual); } priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED); -- cgit v1.2.3 From 7126a2aeabae696f73d9497f32c5d212d7311916 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 17 May 2023 20:15:28 +0200 Subject: usb: Switch i2c drivers back to use .probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After commit b8a1a4cd5a98 ("i2c: Provide a temporary .probe_new() call-back type"), all drivers being converted to .probe_new() and then 03c835f498b5 ("i2c: Switch .probe() to not take an id parameter") convert back to (the new) .probe() to be able to eventually drop .probe_new() from struct i2c_driver. While touching hd3ss3220.c fix a minor white space issue in the definition of struct hd3ss3220_driver. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230517181528.167115-1-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb251xb.c | 2 +- drivers/usb/misc/usb3503.c | 2 +- drivers/usb/misc/usb4604.c | 2 +- drivers/usb/phy/phy-isp1301.c | 2 +- drivers/usb/typec/anx7411.c | 2 +- drivers/usb/typec/hd3ss3220.c | 4 ++-- drivers/usb/typec/mux/fsa4480.c | 2 +- drivers/usb/typec/mux/pi3usb30532.c | 2 +- drivers/usb/typec/rt1719.c | 2 +- drivers/usb/typec/stusb160x.c | 2 +- drivers/usb/typec/tcpm/fusb302.c | 2 +- drivers/usb/typec/tcpm/tcpci.c | 2 +- drivers/usb/typec/tcpm/tcpci_maxim_core.c | 2 +- drivers/usb/typec/tcpm/tcpci_rt1711h.c | 2 +- drivers/usb/typec/tipd/core.c | 2 +- drivers/usb/typec/ucsi/ucsi_ccg.c | 2 +- drivers/usb/typec/ucsi/ucsi_stm32g0.c | 2 +- drivers/usb/typec/wusb3801.c | 2 +- 18 files changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c index ce1da80d3365..1f3329ef5c7a 100644 --- a/drivers/usb/misc/usb251xb.c +++ b/drivers/usb/misc/usb251xb.c @@ -746,7 +746,7 @@ static struct i2c_driver usb251xb_i2c_driver = { .of_match_table = usb251xb_of_match, .pm = &usb251xb_pm_ops, }, - .probe_new = usb251xb_i2c_probe, + .probe = usb251xb_i2c_probe, .id_table = usb251xb_id, }; diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index fa3005934942..72765077932c 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -411,7 +411,7 @@ static struct i2c_driver usb3503_i2c_driver = { .pm = pm_ptr(&usb3503_i2c_pm_ops), .of_match_table = of_match_ptr(usb3503_of_match), }, - .probe_new = usb3503_i2c_probe, + .probe = usb3503_i2c_probe, .remove = usb3503_i2c_remove, .id_table = usb3503_id, }; diff --git a/drivers/usb/misc/usb4604.c b/drivers/usb/misc/usb4604.c index 6b5e77231efa..065e269ba4e3 100644 --- a/drivers/usb/misc/usb4604.c +++ b/drivers/usb/misc/usb4604.c @@ -154,7 +154,7 @@ static struct i2c_driver usb4604_i2c_driver = { .pm = pm_ptr(&usb4604_i2c_pm_ops), .of_match_table = of_match_ptr(usb4604_of_match), }, - .probe_new = usb4604_i2c_probe, + .probe = usb4604_i2c_probe, .id_table = usb4604_id, }; module_i2c_driver(usb4604_i2c_driver); diff --git a/drivers/usb/phy/phy-isp1301.c b/drivers/usb/phy/phy-isp1301.c index f4ee14d98585..993d7525a102 100644 --- a/drivers/usb/phy/phy-isp1301.c +++ b/drivers/usb/phy/phy-isp1301.c @@ -132,7 +132,7 @@ static struct i2c_driver isp1301_driver = { .name = DRV_NAME, .of_match_table = isp1301_of_match, }, - .probe_new = isp1301_probe, + .probe = isp1301_probe, .remove = isp1301_remove, .id_table = isp1301_id, }; diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c index 3d5edce270a4..221604f933a4 100644 --- a/drivers/usb/typec/anx7411.c +++ b/drivers/usb/typec/anx7411.c @@ -1584,7 +1584,7 @@ static struct i2c_driver anx7411_driver = { .of_match_table = anx_match_table, .pm = &anx7411_pm_ops, }, - .probe_new = anx7411_i2c_probe, + .probe = anx7411_i2c_probe, .remove = anx7411_i2c_remove, .id_table = anx7411_id, diff --git a/drivers/usb/typec/hd3ss3220.c b/drivers/usb/typec/hd3ss3220.c index 8bbeb9b1e439..fb1242e82ffd 100644 --- a/drivers/usb/typec/hd3ss3220.c +++ b/drivers/usb/typec/hd3ss3220.c @@ -292,8 +292,8 @@ static struct i2c_driver hd3ss3220_driver = { .name = "hd3ss3220", .of_match_table = dev_ids, }, - .probe_new = hd3ss3220_probe, - .remove = hd3ss3220_remove, + .probe = hd3ss3220_probe, + .remove = hd3ss3220_remove, }; module_i2c_driver(hd3ss3220_driver); diff --git a/drivers/usb/typec/mux/fsa4480.c b/drivers/usb/typec/mux/fsa4480.c index d6495e533e58..b201dda63d77 100644 --- a/drivers/usb/typec/mux/fsa4480.c +++ b/drivers/usb/typec/mux/fsa4480.c @@ -206,7 +206,7 @@ static struct i2c_driver fsa4480_driver = { .name = "fsa4480", .of_match_table = fsa4480_of_table, }, - .probe_new = fsa4480_probe, + .probe = fsa4480_probe, .remove = fsa4480_remove, .id_table = fsa4480_table, }; diff --git a/drivers/usb/typec/mux/pi3usb30532.c b/drivers/usb/typec/mux/pi3usb30532.c index 1cd388b55c30..8eeec135dcdb 100644 --- a/drivers/usb/typec/mux/pi3usb30532.c +++ b/drivers/usb/typec/mux/pi3usb30532.c @@ -178,7 +178,7 @@ static struct i2c_driver pi3usb30532_driver = { .driver = { .name = "pi3usb30532", }, - .probe_new = pi3usb30532_probe, + .probe = pi3usb30532_probe, .remove = pi3usb30532_remove, .id_table = pi3usb30532_table, }; diff --git a/drivers/usb/typec/rt1719.c b/drivers/usb/typec/rt1719.c index ea8b700b0ceb..be02d420920e 100644 --- a/drivers/usb/typec/rt1719.c +++ b/drivers/usb/typec/rt1719.c @@ -949,7 +949,7 @@ static struct i2c_driver rt1719_driver = { .name = "rt1719", .of_match_table = rt1719_device_table, }, - .probe_new = rt1719_probe, + .probe = rt1719_probe, .remove = rt1719_remove, }; module_i2c_driver(rt1719_driver); diff --git a/drivers/usb/typec/stusb160x.c b/drivers/usb/typec/stusb160x.c index 494b371151e0..3ab118df1bd4 100644 --- a/drivers/usb/typec/stusb160x.c +++ b/drivers/usb/typec/stusb160x.c @@ -870,7 +870,7 @@ static struct i2c_driver stusb160x_driver = { .pm = &stusb160x_pm_ops, .of_match_table = stusb160x_of_match, }, - .probe_new = stusb160x_probe, + .probe = stusb160x_probe, .remove = stusb160x_remove, }; module_i2c_driver(stusb160x_driver); diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c index 62ba53357612..7fc1ffa14f76 100644 --- a/drivers/usb/typec/tcpm/fusb302.c +++ b/drivers/usb/typec/tcpm/fusb302.c @@ -1836,7 +1836,7 @@ static struct i2c_driver fusb302_driver = { .pm = &fusb302_pm_ops, .of_match_table = of_match_ptr(fusb302_dt_match), }, - .probe_new = fusb302_probe, + .probe = fusb302_probe, .remove = fusb302_remove, .id_table = fusb302_i2c_device_id, }; diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index 8da23240afbe..fc708c289a73 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -895,7 +895,7 @@ static struct i2c_driver tcpci_i2c_driver = { .name = "tcpci", .of_match_table = of_match_ptr(tcpci_of_match), }, - .probe_new = tcpci_probe, + .probe = tcpci_probe, .remove = tcpci_remove, .id_table = tcpci_id, }; diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/tcpm/tcpci_maxim_core.c index f32cda2a5e3a..9454b12a073c 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c @@ -508,7 +508,7 @@ static struct i2c_driver max_tcpci_i2c_driver = { .name = "maxtcpc", .of_match_table = of_match_ptr(max_tcpci_of_match), }, - .probe_new = max_tcpci_probe, + .probe = max_tcpci_probe, .remove = max_tcpci_remove, .id_table = max_tcpci_id, }; diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c index a0e9e3fe8564..17ebc5fb684f 100644 --- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c +++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c @@ -412,7 +412,7 @@ static struct i2c_driver rt1711h_i2c_driver = { .name = "rt1711h", .of_match_table = of_match_ptr(rt1711h_of_match), }, - .probe_new = rt1711h_probe, + .probe = rt1711h_probe, .remove = rt1711h_remove, .id_table = rt1711h_id, }; diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 438cc40660a1..69a4d1e9e808 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -950,7 +950,7 @@ static struct i2c_driver tps6598x_i2c_driver = { .pm = &tps6598x_pm_ops, .of_match_table = tps6598x_of_match, }, - .probe_new = tps6598x_probe, + .probe = tps6598x_probe, .remove = tps6598x_remove, .id_table = tps6598x_id, }; diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index e0ed465bd518..607061a37eca 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -1495,7 +1495,7 @@ static struct i2c_driver ucsi_ccg_driver = { .acpi_match_table = amd_i2c_ucsi_match, .of_match_table = ucsi_ccg_of_match_table, }, - .probe_new = ucsi_ccg_probe, + .probe = ucsi_ccg_probe, .remove = ucsi_ccg_remove, .id_table = ucsi_ccg_device_id, }; diff --git a/drivers/usb/typec/ucsi/ucsi_stm32g0.c b/drivers/usb/typec/ucsi/ucsi_stm32g0.c index 93fead0096b7..93d7806681cf 100644 --- a/drivers/usb/typec/ucsi/ucsi_stm32g0.c +++ b/drivers/usb/typec/ucsi/ucsi_stm32g0.c @@ -763,7 +763,7 @@ static struct i2c_driver ucsi_stm32g0_i2c_driver = { .of_match_table = of_match_ptr(ucsi_stm32g0_typec_of_match), .pm = pm_sleep_ptr(&ucsi_stm32g0_pm_ops), }, - .probe_new = ucsi_stm32g0_probe, + .probe = ucsi_stm32g0_probe, .remove = ucsi_stm32g0_remove, .id_table = ucsi_stm32g0_typec_i2c_devid }; diff --git a/drivers/usb/typec/wusb3801.c b/drivers/usb/typec/wusb3801.c index a43a18d4b02e..6062875fb04a 100644 --- a/drivers/usb/typec/wusb3801.c +++ b/drivers/usb/typec/wusb3801.c @@ -420,7 +420,7 @@ static const struct of_device_id wusb3801_of_match[] = { MODULE_DEVICE_TABLE(of, wusb3801_of_match); static struct i2c_driver wusb3801_driver = { - .probe_new = wusb3801_probe, + .probe = wusb3801_probe, .remove = wusb3801_remove, .driver = { .name = "wusb3801", -- cgit v1.2.3 From 622cc875dd2b1e560235e85726c818e58d3541ef Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 18 May 2023 22:26:36 +0200 Subject: usb: host: fhci-hcd: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is (mostly) ignored and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Trivially convert this driver from always returning zero to the void returning variant. Signed-off-by: Uwe Kleine-König Reviewed-by: Dmitry Torokhov Link: https://lore.kernel.org/r/20230518202636.273407-1-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/fhci-hcd.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/usb/host/fhci-hcd.c b/drivers/usb/host/fhci-hcd.c index 92794ffc25c8..66a045e01dad 100644 --- a/drivers/usb/host/fhci-hcd.c +++ b/drivers/usb/host/fhci-hcd.c @@ -757,7 +757,7 @@ err_regs: return ret; } -static int fhci_remove(struct device *dev) +static void fhci_remove(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct fhci_hcd *fhci = hcd_to_fhci(hcd); @@ -771,12 +771,11 @@ static int fhci_remove(struct device *dev) qe_pin_free(fhci->pins[j]); fhci_dfs_destroy(fhci); usb_put_hcd(hcd); - return 0; } -static int of_fhci_remove(struct platform_device *ofdev) +static void of_fhci_remove(struct platform_device *ofdev) { - return fhci_remove(&ofdev->dev); + fhci_remove(&ofdev->dev); } static const struct of_device_id of_fhci_match[] = { @@ -791,7 +790,7 @@ static struct platform_driver of_fhci_driver = { .of_match_table = of_fhci_match, }, .probe = of_fhci_probe, - .remove = of_fhci_remove, + .remove_new = of_fhci_remove, }; module_platform_driver(of_fhci_driver); -- cgit v1.2.3 From db3c4e366287cc2ed7b15e74a85bc5a12f5be4b8 Mon Sep 17 00:00:00 2001 From: Shenwei Wang Date: Tue, 23 May 2023 13:44:12 -0500 Subject: usb: cdns3: imx: Rework system PM to avoid duplicated operations The current implementation uses the same callbacks for system PM and runtime PM suspend/resume without any state checking. This can cause the clocks to be prepared/unprepared twice, leading to kernel warning issues. This patch resolves the double prepare/unprepare issues by separating the runtime PM and system PM handling. Signed-off-by: Shenwei Wang Reviewed-by: Frank Li Link: https://lore.kernel.org/r/20230523184412.204582-1-shenwei.wang@nxp.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/cdns3-imx.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c index 31c39728156f..281de47e2a3b 100644 --- a/drivers/usb/cdns3/cdns3-imx.c +++ b/drivers/usb/cdns3/cdns3-imx.c @@ -373,14 +373,22 @@ static inline bool cdns_imx_is_power_lost(struct cdns_imx *data) return false; } +static int __maybe_unused cdns_imx_system_suspend(struct device *dev) +{ + pm_runtime_put_sync(dev); + return 0; +} + static int __maybe_unused cdns_imx_system_resume(struct device *dev) { struct cdns_imx *data = dev_get_drvdata(dev); int ret; - ret = cdns_imx_resume(dev); - if (ret) + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) { + dev_err(dev, "Could not get runtime PM.\n"); return ret; + } if (cdns_imx_is_power_lost(data)) { dev_dbg(dev, "resume from power lost\n"); @@ -403,7 +411,7 @@ static int cdns_imx_platform_suspend(struct device *dev, static const struct dev_pm_ops cdns_imx_pm_ops = { SET_RUNTIME_PM_OPS(cdns_imx_suspend, cdns_imx_resume, NULL) - SET_SYSTEM_SLEEP_PM_OPS(cdns_imx_suspend, cdns_imx_system_resume) + SET_SYSTEM_SLEEP_PM_OPS(cdns_imx_system_suspend, cdns_imx_system_resume) }; static const struct of_device_id cdns_imx_of_match[] = { -- cgit v1.2.3 From 25ff1eeac7547b68a74f4f3b6fa3ce635a4f636f Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Mon, 22 May 2023 18:29:35 +0200 Subject: dt-bindings: usb: Add RK3588 OHCI Add compatible for RK3588 OHCI. As far as I know it's fully compatible with generic-ohci. Reviewed-by: Rob Herring Signed-off-by: Sebastian Reichel Link: https://lore.kernel.org/r/20230522162937.53190-2-sebastian.reichel@collabora.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/generic-ohci.yaml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/generic-ohci.yaml b/Documentation/devicetree/bindings/usb/generic-ohci.yaml index d06d1e7d8876..be268e23ca79 100644 --- a/Documentation/devicetree/bindings/usb/generic-ohci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ohci.yaml @@ -44,6 +44,7 @@ properties: - hpe,gxp-ohci - ibm,476gtr-ohci - ingenic,jz4740-ohci + - rockchip,rk3588-ohci - snps,hsdk-v1.0-ohci - const: generic-ohci - enum: @@ -69,7 +70,7 @@ properties: clocks: minItems: 1 - maxItems: 3 + maxItems: 4 description: | In case the Renesas R-Car Gen3 SoCs: - if a host only channel: first clock should be host. @@ -147,6 +148,20 @@ allOf: then: properties: transceiver: false + - if: + properties: + compatible: + contains: + const: rockchip,rk3588-ohci + then: + properties: + clocks: + minItems: 4 + else: + properties: + clocks: + minItems: 1 + maxItems: 3 unevaluatedProperties: false -- cgit v1.2.3 From 06abc973d8e1cd3fcc8250d8bcf28906c8a83ba7 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Mon, 22 May 2023 18:29:36 +0200 Subject: dt-bindings: usb: Add RK3588 EHCI Add compatible for RK3588 EHCI. As far as I know it's fully compatible with generic-ehci. Acked-by: Krzysztof Kozlowski Reviewed-by: Rob Herring Signed-off-by: Sebastian Reichel Link: https://lore.kernel.org/r/20230522162937.53190-3-sebastian.reichel@collabora.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/generic-ehci.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/usb/generic-ehci.yaml b/Documentation/devicetree/bindings/usb/generic-ehci.yaml index 9445764bd8de..b956bb5fada7 100644 --- a/Documentation/devicetree/bindings/usb/generic-ehci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ehci.yaml @@ -61,6 +61,7 @@ properties: - ibm,476gtr-ehci - nxp,lpc1850-ehci - qca,ar7100-ehci + - rockchip,rk3588-ehci - snps,hsdk-v1.0-ehci - socionext,uniphier-ehci - const: generic-ehci -- cgit v1.2.3 From fba985aaf46391066773f3bf3a08fb6e75b37673 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Mon, 22 May 2023 18:29:37 +0200 Subject: usb: host: ohci-platform: increase max clock number to 4 Rockchip RK3588 OHCI requires 4 clocks to be enabled. Acked-by: Alan Stern Signed-off-by: Sebastian Reichel Link: https://lore.kernel.org/r/20230522162937.53190-4-sebastian.reichel@collabora.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 03232c5936e8..45a2c981319e 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -33,7 +33,7 @@ #include "ohci.h" #define DRIVER_DESC "OHCI generic platform driver" -#define OHCI_MAX_CLKS 3 +#define OHCI_MAX_CLKS 4 #define hcd_to_ohci_priv(h) ((struct ohci_platform_priv *)hcd_to_ohci(h)->priv) struct ohci_platform_priv { -- cgit v1.2.3 From f16135918b5f8b510db014ecf0a069e34c02382e Mon Sep 17 00:00:00 2001 From: Bhupesh Sharma Date: Thu, 18 May 2023 02:47:51 +0530 Subject: usb: misc: eud: Fix eud sysfs path (use 'qcom_eud') The eud sysfs enablement path is currently mentioned in the Documentation as: /sys/bus/platform/drivers/eud/.../enable Instead it should be: /sys/bus/platform/drivers/qcom_eud/.../enable Fix the same. Fixes: 9a1bf58ccd44 ("usb: misc: eud: Add driver support for Embedded USB Debugger(EUD)") Reviewed-by: Konrad Dybcio Acked-by: Manivannan Sadhasivam Signed-off-by: Bhupesh Sharma Link: https://lore.kernel.org/r/20230517211756.2483552-2-bhupesh.sharma@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-driver-eud | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-driver-eud b/Documentation/ABI/testing/sysfs-driver-eud index 83f3872182a4..2bab0db2d2f0 100644 --- a/Documentation/ABI/testing/sysfs-driver-eud +++ b/Documentation/ABI/testing/sysfs-driver-eud @@ -1,4 +1,4 @@ -What: /sys/bus/platform/drivers/eud/.../enable +What: /sys/bus/platform/drivers/qcom_eud/.../enable Date: February 2022 Contact: Souradeep Chowdhury Description: -- cgit v1.2.3 From 046895105d9666ab56e86ce8dd9786f8003125c6 Mon Sep 17 00:00:00 2001 From: Bhupesh Sharma Date: Thu, 18 May 2023 02:47:53 +0530 Subject: usb: misc: eud: Fix indentation issues Fix a couple of indentation issues in EUD driver. Signed-off-by: Bhupesh Sharma Link: https://lore.kernel.org/r/20230517211756.2483552-4-bhupesh.sharma@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/qcom_eud.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/misc/qcom_eud.c b/drivers/usb/misc/qcom_eud.c index 0dc414463759..7f371ea1248c 100644 --- a/drivers/usb/misc/qcom_eud.c +++ b/drivers/usb/misc/qcom_eud.c @@ -22,10 +22,10 @@ #define EUD_REG_VBUS_INT_CLR 0x0080 #define EUD_REG_CSR_EUD_EN 0x1014 #define EUD_REG_SW_ATTACH_DET 0x1018 -#define EUD_REG_EUD_EN2 0x0000 +#define EUD_REG_EUD_EN2 0x0000 #define EUD_ENABLE BIT(0) -#define EUD_INT_PET_EUD BIT(0) +#define EUD_INT_PET_EUD BIT(0) #define EUD_INT_VBUS BIT(2) #define EUD_INT_SAFE_MODE BIT(4) #define EUD_INT_ALL (EUD_INT_VBUS | EUD_INT_SAFE_MODE) -- cgit v1.2.3 From 033c2d8ab2835a7f13e1a9c6813b412935e77140 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 24 May 2023 14:03:55 +0300 Subject: thunderbolt: Log DisplayPort adapter rate and lanes on discovery This may be helpful when debugging possible issues around DisplayPort port tunneling. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tunnel.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 3bf2628a5dcd..527fef43b2e9 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -1137,6 +1137,47 @@ static int tb_dp_init_video_path(struct tb_path *path) return 0; } +static void tb_dp_dump(struct tb_tunnel *tunnel) +{ + struct tb_port *in, *out; + u32 dp_cap, rate, lanes; + + in = tunnel->src_port; + out = tunnel->dst_port; + + if (tb_port_read(in, &dp_cap, TB_CFG_PORT, + in->cap_adap + DP_LOCAL_CAP, 1)) + return; + + rate = tb_dp_cap_get_rate(dp_cap); + lanes = tb_dp_cap_get_lanes(dp_cap); + + tb_port_dbg(in, "maximum supported bandwidth %u Mb/s x%u = %u Mb/s\n", + rate, lanes, tb_dp_bandwidth(rate, lanes)); + + out = tunnel->dst_port; + + if (tb_port_read(out, &dp_cap, TB_CFG_PORT, + out->cap_adap + DP_LOCAL_CAP, 1)) + return; + + rate = tb_dp_cap_get_rate(dp_cap); + lanes = tb_dp_cap_get_lanes(dp_cap); + + tb_port_dbg(out, "maximum supported bandwidth %u Mb/s x%u = %u Mb/s\n", + rate, lanes, tb_dp_bandwidth(rate, lanes)); + + if (tb_port_read(in, &dp_cap, TB_CFG_PORT, + in->cap_adap + DP_REMOTE_CAP, 1)) + return; + + rate = tb_dp_cap_get_rate(dp_cap); + lanes = tb_dp_cap_get_lanes(dp_cap); + + tb_port_dbg(in, "reduced bandwidth %u Mb/s x%u = %u Mb/s\n", + rate, lanes, tb_dp_bandwidth(rate, lanes)); +} + /** * tb_tunnel_discover_dp() - Discover existing Display Port tunnels * @tb: Pointer to the domain structure @@ -1214,6 +1255,8 @@ struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in, goto err_deactivate; } + tb_dp_dump(tunnel); + tb_tunnel_dbg(tunnel, "discovered\n"); return tunnel; -- cgit v1.2.3 From 56dcc717ecf5bc2ff2f686346ceb72da0e706203 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 30 May 2023 09:36:33 +0200 Subject: usb: misc: onboard_hub: Don't warn twice about problems during remove MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If onboard_hub_power_off() called by onboard_hub_remove() fails it emits an error message. Forwarding the returned error value to the driver core results in another error message. As the return value is otherwise ignored, just drop the return value. There is no side effect apart from suppressing the core's warning. Instead of returning zero unconditionally, convert to .remove_new() which has the same semantics as .remove() that unconditionally returns zero. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230530073633.2193618-1-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/onboard_usb_hub.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c index 12fc6eb67c3b..83f14ca1d38e 100644 --- a/drivers/usb/misc/onboard_usb_hub.c +++ b/drivers/usb/misc/onboard_usb_hub.c @@ -278,7 +278,7 @@ static int onboard_hub_probe(struct platform_device *pdev) return 0; } -static int onboard_hub_remove(struct platform_device *pdev) +static void onboard_hub_remove(struct platform_device *pdev) { struct onboard_hub *hub = dev_get_drvdata(&pdev->dev); struct usbdev_node *node; @@ -306,7 +306,7 @@ static int onboard_hub_remove(struct platform_device *pdev) mutex_unlock(&hub->lock); - return onboard_hub_power_off(hub); + onboard_hub_power_off(hub); } MODULE_DEVICE_TABLE(of, onboard_hub_match); @@ -317,7 +317,7 @@ static const struct dev_pm_ops __maybe_unused onboard_hub_pm_ops = { static struct platform_driver onboard_hub_driver = { .probe = onboard_hub_probe, - .remove = onboard_hub_remove, + .remove_new = onboard_hub_remove, .driver = { .name = "onboard-usb-hub", -- cgit v1.2.3 From b519f44b7807c1e2a2a2f860f2125a88eb10c3e3 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 30 May 2023 09:19:08 +0200 Subject: usb: dwc2/platform: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Acked-by: Minas Harutyunyan Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230530071913.2192214-2-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc2/platform.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 5cf025511cce..0a806f80217e 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -301,7 +301,7 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) * stops device processing. Any resources used on behalf of this device are * freed. */ -static int dwc2_driver_remove(struct platform_device *dev) +static void dwc2_driver_remove(struct platform_device *dev) { struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); struct dwc2_gregs_backup *gr; @@ -351,8 +351,6 @@ static int dwc2_driver_remove(struct platform_device *dev) if (hsotg->ll_hw_enabled) dwc2_lowlevel_hw_disable(hsotg); - - return 0; } /** @@ -756,7 +754,7 @@ static struct platform_driver dwc2_platform_driver = { .pm = &dwc2_dev_pm_ops, }, .probe = dwc2_driver_probe, - .remove = dwc2_driver_remove, + .remove_new = dwc2_driver_remove, .shutdown = dwc2_driver_shutdown, }; -- cgit v1.2.3 From 3a8d85c43d594e40cc31bb2b68b6987c4b8ef168 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 30 May 2023 09:19:09 +0200 Subject: usb: xhci-histb: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230530071913.2192214-3-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-histb.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-histb.c b/drivers/usb/host/xhci-histb.c index 91ce97821de5..d8aba07e802d 100644 --- a/drivers/usb/host/xhci-histb.c +++ b/drivers/usb/host/xhci-histb.c @@ -319,7 +319,7 @@ disable_pm: return ret; } -static int xhci_histb_remove(struct platform_device *dev) +static void xhci_histb_remove(struct platform_device *dev) { struct xhci_hcd_histb *histb = platform_get_drvdata(dev); struct usb_hcd *hcd = histb->hcd; @@ -339,8 +339,6 @@ static int xhci_histb_remove(struct platform_device *dev) usb_put_hcd(hcd); pm_runtime_put_sync(&dev->dev); pm_runtime_disable(&dev->dev); - - return 0; } static int __maybe_unused xhci_histb_suspend(struct device *dev) @@ -385,7 +383,7 @@ MODULE_DEVICE_TABLE(of, histb_xhci_of_match); static struct platform_driver histb_xhci_driver = { .probe = xhci_histb_probe, - .remove = xhci_histb_remove, + .remove_new = xhci_histb_remove, .driver = { .name = "xhci-histb", .pm = DEV_PM_OPS, -- cgit v1.2.3 From d89dfff5eaebc888d9acc6ff1021a33a99d43136 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 30 May 2023 09:19:10 +0200 Subject: usb: xhci-mtk: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230530071913.2192214-4-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mtk.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index 90cf40d6d0c3..8d9a55b0281b 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -673,7 +673,7 @@ disable_pm: return ret; } -static int xhci_mtk_remove(struct platform_device *pdev) +static void xhci_mtk_remove(struct platform_device *pdev) { struct xhci_hcd_mtk *mtk = platform_get_drvdata(pdev); struct usb_hcd *hcd = mtk->hcd; @@ -703,8 +703,6 @@ static int xhci_mtk_remove(struct platform_device *pdev) pm_runtime_disable(dev); pm_runtime_put_noidle(dev); pm_runtime_set_suspended(dev); - - return 0; } static int __maybe_unused xhci_mtk_suspend(struct device *dev) @@ -824,7 +822,7 @@ MODULE_DEVICE_TABLE(of, mtk_xhci_of_match); static struct platform_driver mtk_xhci_driver = { .probe = xhci_mtk_probe, - .remove = xhci_mtk_remove, + .remove_new = xhci_mtk_remove, .driver = { .name = "xhci-mtk", .pm = DEV_PM_OPS, -- cgit v1.2.3 From bcfe934e2efb8d7075911233ae00f82f3e025533 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 30 May 2023 09:19:11 +0200 Subject: usb: xhci-plat: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). To convert the xhci-plat driver, change the prototype of xhci_plat_remove() to return void. As this function is exported and used by the xhci-rcar driver, convert this driver at the same time accordingly. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230530071913.2192214-5-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-plat.c | 6 ++---- drivers/usb/host/xhci-plat.h | 2 +- drivers/usb/host/xhci-rcar.c | 6 +++--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index a666b21c21bb..a52d73c2cd80 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -395,7 +395,7 @@ static int xhci_generic_plat_probe(struct platform_device *pdev) return xhci_plat_probe(pdev, sysdev, priv_match); } -int xhci_plat_remove(struct platform_device *dev) +void xhci_plat_remove(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(hcd); @@ -426,8 +426,6 @@ int xhci_plat_remove(struct platform_device *dev) pm_runtime_disable(&dev->dev); pm_runtime_put_noidle(&dev->dev); pm_runtime_set_suspended(&dev->dev); - - return 0; } EXPORT_SYMBOL_GPL(xhci_plat_remove); @@ -526,7 +524,7 @@ MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match); static struct platform_driver usb_generic_xhci_driver = { .probe = xhci_generic_plat_probe, - .remove = xhci_plat_remove, + .remove_new = xhci_plat_remove, .shutdown = usb_hcd_platform_shutdown, .driver = { .name = "xhci-hcd", diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h index 83b5b5aa9f8e..2d15386f2c50 100644 --- a/drivers/usb/host/xhci-plat.h +++ b/drivers/usb/host/xhci-plat.h @@ -25,7 +25,7 @@ struct xhci_plat_priv { int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const struct xhci_plat_priv *priv_match); -int xhci_plat_remove(struct platform_device *dev); +void xhci_plat_remove(struct platform_device *dev); extern const struct dev_pm_ops xhci_plat_pm_ops; #endif /* _XHCI_PLAT_H */ diff --git a/drivers/usb/host/xhci-rcar.c b/drivers/usb/host/xhci-rcar.c index ad966b797b89..bf5261fed32c 100644 --- a/drivers/usb/host/xhci-rcar.c +++ b/drivers/usb/host/xhci-rcar.c @@ -276,10 +276,10 @@ static int xhci_renesas_probe(struct platform_device *pdev) } static struct platform_driver usb_xhci_renesas_driver = { - .probe = xhci_renesas_probe, - .remove = xhci_plat_remove, + .probe = xhci_renesas_probe, + .remove_new = xhci_plat_remove, .shutdown = usb_hcd_platform_shutdown, - .driver = { + .driver = { .name = "xhci-renesas-hcd", .pm = &xhci_plat_pm_ops, .of_match_table = usb_xhci_of_match, -- cgit v1.2.3 From 40f7b7f65a0584821a8fa4abb1c64fb2ba79aebf Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 30 May 2023 09:19:12 +0200 Subject: usb: xhci-tegra: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230530071913.2192214-6-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-tegra.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 393e2c8064bd..f124483044a2 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1912,7 +1912,7 @@ put_padctl: return err; } -static int tegra_xusb_remove(struct platform_device *pdev) +static void tegra_xusb_remove(struct platform_device *pdev) { struct tegra_xusb *tegra = platform_get_drvdata(pdev); struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd); @@ -1942,8 +1942,6 @@ static int tegra_xusb_remove(struct platform_device *pdev) tegra_xusb_clk_disable(tegra); regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies); tegra_xusb_padctl_put(tegra->padctl); - - return 0; } static bool xhci_hub_ports_suspended(struct xhci_hub *hub) @@ -2653,7 +2651,7 @@ MODULE_DEVICE_TABLE(of, tegra_xusb_of_match); static struct platform_driver tegra_xusb_driver = { .probe = tegra_xusb_probe, - .remove = tegra_xusb_remove, + .remove_new = tegra_xusb_remove, .driver = { .name = "tegra-xusb", .pm = &tegra_xusb_pm_ops, -- cgit v1.2.3 From 922c0cb578ac9104a22c11a093cc1e0575c35a39 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 30 May 2023 09:19:13 +0200 Subject: usb: typec: qcom-pmic-typec: Convert to platform remove callback returning void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .remove() callback for a platform driver returns an int which makes many driver authors wrongly assume it's possible to do error handling by returning an error code. However the value returned is ignored (apart from emitting a warning) and this typically results in resource leaks. To improve here there is a quest to make the remove callback return void. In the first step of this quest all drivers are converted to .remove_new() which already returns void. Eventually after all drivers are converted, .remove_new() is renamed to .remove(). Trivially convert this driver from always returning zero in the remove callback to the void returning variant. Reviewed-by: Konrad Dybcio Acked-by: Bryan O'Donoghue Acked-by: Heikki Krogerus Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20230530071913.2192214-7-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c index 937e855a6c4c..a905160dd860 100644 --- a/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c +++ b/drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c @@ -236,7 +236,7 @@ fwnode_remove: return ret; } -static int qcom_pmic_typec_remove(struct platform_device *pdev) +static void qcom_pmic_typec_remove(struct platform_device *pdev) { struct pmic_typec *tcpm = platform_get_drvdata(pdev); @@ -244,8 +244,6 @@ static int qcom_pmic_typec_remove(struct platform_device *pdev) qcom_pmic_typec_port_stop(tcpm->pmic_typec_port); tcpm_unregister_port(tcpm->tcpm_port); fwnode_remove_software_node(tcpm->tcpc.fwnode); - - return 0; } static struct pmic_typec_pdphy_resources pm8150b_pdphy_res = { @@ -337,7 +335,7 @@ static struct platform_driver qcom_pmic_typec_driver = { .of_match_table = qcom_pmic_typec_table, }, .probe = qcom_pmic_typec_probe, - .remove = qcom_pmic_typec_remove, + .remove_new = qcom_pmic_typec_remove, }; module_platform_driver(qcom_pmic_typec_driver); -- cgit v1.2.3 From 243ff7e6a03533fd5f34036b4a2c297d844ffdc0 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 31 May 2023 22:03:25 -0700 Subject: usb: typec: mux: fix static inline syntax error Fix build error when USB_SUPPORT is not set or TYPEC is not set by dropping an extraneous semi-colon: In file included from ../drivers/phy/qualcomm/phy-qcom-qmp-combo.c:23: ../include/linux/usb/typec_mux.h:77:1: error: expected identifier or '(' before '{' token 77 | { | ^ ../include/linux/usb/typec_mux.h:76:33: warning: 'fwnode_typec_mux_get' used but never defined 76 | static inline struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode); | ^~~~~~~~~~~~~~~~~~~~ Fixes: 3524fe31538c ("usb: typec: mux: Remove alt mode parameters from the API") Signed-off-by: Randy Dunlap Cc: Heikki Krogerus Cc: Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org Link: https://lore.kernel.org/r/20230601050325.26883-1-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/typec_mux.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/usb/typec_mux.h b/include/linux/usb/typec_mux.h index 11bfa314529f..2489a7857d8e 100644 --- a/include/linux/usb/typec_mux.h +++ b/include/linux/usb/typec_mux.h @@ -73,7 +73,7 @@ void *typec_mux_get_drvdata(struct typec_mux_dev *mux); #else -static inline struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode); +static inline struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode) { return NULL; } -- cgit v1.2.3 From 6ff58ae17fd9523246a260434133ed9ab7f56df2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sun, 4 Jun 2023 14:35:03 +0200 Subject: USB: serial: return errors from break handling Start propagating errors to user space when setting the break state fails. This will be used by follow-on changes to also report when a driver or device does not support break control. Tested-by: Corey Minyard Signed-off-by: Johan Hovold --- drivers/usb/serial/ark3116.c | 7 +++++-- drivers/usb/serial/belkin_sa.c | 12 +++++++++--- drivers/usb/serial/ch341.c | 37 ++++++++++++++++++++++------------- drivers/usb/serial/cp210x.c | 8 +++++--- drivers/usb/serial/digi_acceleport.c | 7 ++++--- drivers/usb/serial/f81232.c | 4 +++- drivers/usb/serial/f81534.c | 4 +++- drivers/usb/serial/ftdi_sio.c | 10 +++++++--- drivers/usb/serial/io_edgeport.c | 6 ++++-- drivers/usb/serial/io_ti.c | 9 +++++++-- drivers/usb/serial/keyspan.c | 5 ++++- drivers/usb/serial/keyspan_pda.c | 8 ++++++-- drivers/usb/serial/mct_u232.c | 6 +++--- drivers/usb/serial/mos7720.c | 9 +++++---- drivers/usb/serial/mos7840.c | 7 ++++--- drivers/usb/serial/mxuport.c | 6 +++--- drivers/usb/serial/pl2303.c | 14 ++++++++----- drivers/usb/serial/quatech2.c | 8 ++++++-- drivers/usb/serial/ti_usb_3410_5052.c | 10 +++++++--- drivers/usb/serial/upd78f0730.c | 7 +++++-- drivers/usb/serial/usb-serial.c | 2 +- drivers/usb/serial/usb_debug.c | 13 +++++++++--- drivers/usb/serial/whiteheat.c | 7 ++++--- drivers/usb/serial/xr_serial.c | 4 ++-- include/linux/usb/serial.h | 2 +- 25 files changed, 140 insertions(+), 72 deletions(-) diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index 9452291f1703..67a07cc007f0 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -433,10 +433,11 @@ static int ark3116_tiocmset(struct tty_struct *tty, return 0; } -static void ark3116_break_ctl(struct tty_struct *tty, int break_state) +static int ark3116_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; struct ark3116_private *priv = usb_get_serial_port_data(port); + int ret; /* LCR is also used for other things: protect access */ mutex_lock(&priv->hw_lock); @@ -446,9 +447,11 @@ static void ark3116_break_ctl(struct tty_struct *tty, int break_state) else priv->lcr &= ~UART_LCR_SBC; - ark3116_write_reg(port->serial, UART_LCR, priv->lcr); + ret = ark3116_write_reg(port->serial, UART_LCR, priv->lcr); mutex_unlock(&priv->hw_lock); + + return ret; } static void ark3116_update_msr(struct usb_serial_port *port, __u8 msr) diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c index 9331a562dac0..cf47ee4ae5d3 100644 --- a/drivers/usb/serial/belkin_sa.c +++ b/drivers/usb/serial/belkin_sa.c @@ -46,7 +46,7 @@ static void belkin_sa_process_read_urb(struct urb *urb); static void belkin_sa_set_termios(struct tty_struct *tty, struct usb_serial_port *port, const struct ktermios *old_termios); -static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state); +static int belkin_sa_break_ctl(struct tty_struct *tty, int break_state); static int belkin_sa_tiocmget(struct tty_struct *tty); static int belkin_sa_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); @@ -399,13 +399,19 @@ static void belkin_sa_set_termios(struct tty_struct *tty, spin_unlock_irqrestore(&priv->lock, flags); } -static void belkin_sa_break_ctl(struct tty_struct *tty, int break_state) +static int belkin_sa_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; + int ret; - if (BSA_USB_CMD(BELKIN_SA_SET_BREAK_REQUEST, break_state ? 1 : 0) < 0) + ret = BSA_USB_CMD(BELKIN_SA_SET_BREAK_REQUEST, break_state ? 1 : 0); + if (ret < 0) { dev_err(&port->dev, "Set break_ctl %d\n", break_state); + return ret; + } + + return 0; } static int belkin_sa_tiocmget(struct tty_struct *tty) diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c index 6e1b87e67304..612bea504d7a 100644 --- a/drivers/usb/serial/ch341.c +++ b/drivers/usb/serial/ch341.c @@ -562,12 +562,12 @@ static void ch341_set_termios(struct tty_struct *tty, * TCSBRKP. Due to how the simulation is implemented the duration can't be * controlled. The duration is always about (1s / 46bd * 9bit) = 196ms. */ -static void ch341_simulate_break(struct tty_struct *tty, int break_state) +static int ch341_simulate_break(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; struct ch341_private *priv = usb_get_serial_port_data(port); unsigned long now, delay; - int r; + int r, r2; if (break_state != 0) { dev_dbg(&port->dev, "enter break state requested\n"); @@ -599,7 +599,7 @@ static void ch341_simulate_break(struct tty_struct *tty, int break_state) */ priv->break_end = jiffies + (11 * HZ / CH341_MIN_BPS); - return; + return 0; } dev_dbg(&port->dev, "leave break state requested\n"); @@ -615,17 +615,22 @@ static void ch341_simulate_break(struct tty_struct *tty, int break_state) schedule_timeout_interruptible(delay); } + r = 0; restore: /* Restore original baud rate */ - r = ch341_set_baudrate_lcr(port->serial->dev, priv, priv->baud_rate, - priv->lcr); - if (r < 0) + r2 = ch341_set_baudrate_lcr(port->serial->dev, priv, priv->baud_rate, + priv->lcr); + if (r2 < 0) { dev_err(&port->dev, "restoring original baud rate of %u failed: %d\n", - priv->baud_rate, r); + priv->baud_rate, r2); + return r2; + } + + return r; } -static void ch341_break_ctl(struct tty_struct *tty, int break_state) +static int ch341_break_ctl(struct tty_struct *tty, int break_state) { const uint16_t ch341_break_reg = ((uint16_t) CH341_REG_LCR << 8) | CH341_REG_BREAK; @@ -635,17 +640,17 @@ static void ch341_break_ctl(struct tty_struct *tty, int break_state) uint16_t reg_contents; uint8_t break_reg[2]; - if (priv->quirks & CH341_QUIRK_SIMULATE_BREAK) { - ch341_simulate_break(tty, break_state); - return; - } + if (priv->quirks & CH341_QUIRK_SIMULATE_BREAK) + return ch341_simulate_break(tty, break_state); r = ch341_control_in(port->serial->dev, CH341_REQ_READ_REG, ch341_break_reg, 0, break_reg, 2); if (r) { dev_err(&port->dev, "%s - USB control read error (%d)\n", __func__, r); - return; + if (r > 0) + r = -EIO; + return r; } dev_dbg(&port->dev, "%s - initial ch341 break register contents - reg1: %x, reg2: %x\n", __func__, break_reg[0], break_reg[1]); @@ -663,9 +668,13 @@ static void ch341_break_ctl(struct tty_struct *tty, int break_state) reg_contents = get_unaligned_le16(break_reg); r = ch341_control_out(port->serial->dev, CH341_REQ_WRITE_REG, ch341_break_reg, reg_contents); - if (r < 0) + if (r < 0) { dev_err(&port->dev, "%s - USB control write error (%d)\n", __func__, r); + return r; + } + + return 0; } static int ch341_tiocmset(struct tty_struct *tty, diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index cdea1bff3b70..81e49ed9d147 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -39,7 +39,7 @@ static int cp210x_tiocmget(struct tty_struct *); static int cp210x_tiocmset(struct tty_struct *, unsigned int, unsigned int); static int cp210x_tiocmset_port(struct usb_serial_port *port, unsigned int, unsigned int); -static void cp210x_break_ctl(struct tty_struct *, int); +static int cp210x_break_ctl(struct tty_struct *, int); static int cp210x_attach(struct usb_serial *); static void cp210x_disconnect(struct usb_serial *); static void cp210x_release(struct usb_serial *); @@ -1434,7 +1434,7 @@ static int cp210x_tiocmget(struct tty_struct *tty) return result; } -static void cp210x_break_ctl(struct tty_struct *tty, int break_state) +static int cp210x_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; u16 state; @@ -1443,9 +1443,11 @@ static void cp210x_break_ctl(struct tty_struct *tty, int break_state) state = BREAK_OFF; else state = BREAK_ON; + dev_dbg(&port->dev, "%s - turning break %s\n", __func__, state == BREAK_OFF ? "off" : "on"); - cp210x_write_u16_reg(port, CP210X_SET_BREAK, state); + + return cp210x_write_u16_reg(port, CP210X_SET_BREAK, state); } #ifdef CONFIG_GPIOLIB diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c index 45d688e9b93f..d1dea3850576 100644 --- a/drivers/usb/serial/digi_acceleport.c +++ b/drivers/usb/serial/digi_acceleport.c @@ -217,7 +217,7 @@ static void digi_rx_unthrottle(struct tty_struct *tty); static void digi_set_termios(struct tty_struct *tty, struct usb_serial_port *port, const struct ktermios *old_termios); -static void digi_break_ctl(struct tty_struct *tty, int break_state); +static int digi_break_ctl(struct tty_struct *tty, int break_state); static int digi_tiocmget(struct tty_struct *tty); static int digi_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); @@ -839,7 +839,7 @@ static void digi_set_termios(struct tty_struct *tty, } -static void digi_break_ctl(struct tty_struct *tty, int break_state) +static int digi_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; unsigned char buf[4]; @@ -848,7 +848,8 @@ static void digi_break_ctl(struct tty_struct *tty, int break_state) buf[1] = 2; /* length */ buf[2] = break_state ? 1 : 0; buf[3] = 0; /* pad */ - digi_write_inb_command(port, buf, 4, 0); + + return digi_write_inb_command(port, buf, 4, 0); } diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c index 891fb1fe69df..5f7a46bcace6 100644 --- a/drivers/usb/serial/f81232.c +++ b/drivers/usb/serial/f81232.c @@ -448,7 +448,7 @@ static void f81534a_process_read_urb(struct urb *urb) tty_flip_buffer_push(&port->port); } -static void f81232_break_ctl(struct tty_struct *tty, int break_state) +static int f81232_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; struct f81232_private *priv = usb_get_serial_port_data(port); @@ -467,6 +467,8 @@ static void f81232_break_ctl(struct tty_struct *tty, int break_state) dev_err(&port->dev, "set break failed: %d\n", status); mutex_unlock(&priv->lock); + + return status; } static int f81232_find_clk(speed_t baudrate) diff --git a/drivers/usb/serial/f81534.c b/drivers/usb/serial/f81534.c index 4083ae961be4..ef126cd3d94f 100644 --- a/drivers/usb/serial/f81534.c +++ b/drivers/usb/serial/f81534.c @@ -656,7 +656,7 @@ out_unlock: return status; } -static void f81534_break_ctl(struct tty_struct *tty, int break_state) +static int f81534_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; struct f81534_port_private *port_priv = usb_get_serial_port_data(port); @@ -675,6 +675,8 @@ static void f81534_break_ctl(struct tty_struct *tty, int break_state) dev_err(&port->dev, "set break failed: %d\n", status); mutex_unlock(&port_priv->lcr_mutex); + + return status; } static int f81534_update_mctrl(struct usb_serial_port *port, unsigned int set, diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 05e28a5ce42b..1bf23611be12 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -2550,11 +2550,12 @@ static void ftdi_process_read_urb(struct urb *urb) tty_flip_buffer_push(&port->port); } -static void ftdi_break_ctl(struct tty_struct *tty, int break_state) +static int ftdi_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; struct ftdi_private *priv = usb_get_serial_port_data(port); u16 value; + int ret; /* break_state = -1 to turn on break, and 0 to turn off break */ /* see drivers/char/tty_io.c to see it used */ @@ -2565,19 +2566,22 @@ static void ftdi_break_ctl(struct tty_struct *tty, int break_state) else value = priv->last_set_data_value; - if (usb_control_msg(port->serial->dev, + ret = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0), FTDI_SIO_SET_DATA_REQUEST, FTDI_SIO_SET_DATA_REQUEST_TYPE, value, priv->channel, - NULL, 0, WDR_TIMEOUT) < 0) { + NULL, 0, WDR_TIMEOUT); + if (ret < 0) { dev_err(&port->dev, "%s FAILED to enable/disable break state (state was %d)\n", __func__, break_state); + return ret; } dev_dbg(&port->dev, "%s break state is %d - urb is %d\n", __func__, break_state, value); + return 0; } static bool ftdi_tx_empty(struct usb_serial_port *port) diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 3a4c0febf335..abe4bbb0ac65 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -1560,12 +1560,12 @@ static int edge_ioctl(struct tty_struct *tty, * SerialBreak * this function sends a break to the port *****************************************************************************/ -static void edge_break(struct tty_struct *tty, int break_state) +static int edge_break(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); struct edgeport_serial *edge_serial = usb_get_serial_data(port->serial); - int status; + int status = 0; if (!edge_serial->is_epic || edge_serial->epic_descriptor.Supports.IOSPChase) { @@ -1597,6 +1597,8 @@ static void edge_break(struct tty_struct *tty, int break_state) dev_dbg(&port->dev, "%s - error sending break set/clear command.\n", __func__); } + + return status; } diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index bc3c24ea42c1..7a3a6e539456 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -2421,7 +2421,7 @@ static int edge_tiocmget(struct tty_struct *tty) return result; } -static void edge_break(struct tty_struct *tty, int break_state) +static int edge_break(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; struct edgeport_port *edge_port = usb_get_serial_port_data(port); @@ -2430,10 +2430,15 @@ static void edge_break(struct tty_struct *tty, int break_state) if (break_state == -1) bv = 1; /* On */ + status = ti_do_config(edge_port, UMPC_SET_CLR_BREAK, bv); - if (status) + if (status) { dev_dbg(&port->dev, "%s - error %d sending break set/clear command.\n", __func__, status); + return status; + } + + return 0; } static void edge_heartbeat_schedule(struct edgeport_serial *edge_serial) diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c index 2966e0c4941e..93b17e0e05a3 100644 --- a/drivers/usb/serial/keyspan.c +++ b/drivers/usb/serial/keyspan.c @@ -599,7 +599,7 @@ struct keyspan_port_private { #include "keyspan_usa67msg.h" -static void keyspan_break_ctl(struct tty_struct *tty, int break_state) +static int keyspan_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; struct keyspan_port_private *p_priv; @@ -611,7 +611,10 @@ static void keyspan_break_ctl(struct tty_struct *tty, int break_state) else p_priv->break_on = 0; + /* FIXME: return errors */ keyspan_send_setup(port, 0); + + return 0; } diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c index 6fd15cd9e1eb..0eef358b314a 100644 --- a/drivers/usb/serial/keyspan_pda.c +++ b/drivers/usb/serial/keyspan_pda.c @@ -299,7 +299,7 @@ static speed_t keyspan_pda_setbaud(struct usb_serial *serial, speed_t baud) return baud; } -static void keyspan_pda_break_ctl(struct tty_struct *tty, int break_state) +static int keyspan_pda_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; @@ -315,9 +315,13 @@ static void keyspan_pda_break_ctl(struct tty_struct *tty, int break_state) 4, /* set break */ USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, value, 0, NULL, 0, 2000); - if (result < 0) + if (result < 0) { dev_dbg(&port->dev, "%s - error %d from usb_control_msg\n", __func__, result); + return result; + } + + return 0; } static void keyspan_pda_set_termios(struct tty_struct *tty, diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index d3852feb81a4..6570c8817a80 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -47,7 +47,7 @@ static void mct_u232_read_int_callback(struct urb *urb); static void mct_u232_set_termios(struct tty_struct *tty, struct usb_serial_port *port, const struct ktermios *old_termios); -static void mct_u232_break_ctl(struct tty_struct *tty, int break_state); +static int mct_u232_break_ctl(struct tty_struct *tty, int break_state); static int mct_u232_tiocmget(struct tty_struct *tty); static int mct_u232_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); @@ -677,7 +677,7 @@ static void mct_u232_set_termios(struct tty_struct *tty, spin_unlock_irqrestore(&priv->lock, flags); } /* mct_u232_set_termios */ -static void mct_u232_break_ctl(struct tty_struct *tty, int break_state) +static int mct_u232_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; struct mct_u232_private *priv = usb_get_serial_port_data(port); @@ -691,7 +691,7 @@ static void mct_u232_break_ctl(struct tty_struct *tty, int break_state) lcr |= MCT_U232_SET_BREAK; spin_unlock_irqrestore(&priv->lock, flags); - mct_u232_set_line_ctrl(port, lcr); + return mct_u232_set_line_ctrl(port, lcr); } /* mct_u232_break_ctl */ diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 1d1f85fabc28..23544074eb1c 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -996,7 +996,7 @@ static void mos7720_close(struct usb_serial_port *port) mos7720_port->open = 0; } -static void mos7720_break(struct tty_struct *tty, int break_state) +static int mos7720_break(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; unsigned char data; @@ -1007,7 +1007,7 @@ static void mos7720_break(struct tty_struct *tty, int break_state) mos7720_port = usb_get_serial_port_data(port); if (mos7720_port == NULL) - return; + return -ENODEV; if (break_state == -1) data = mos7720_port->shadowLCR | UART_LCR_SBC; @@ -1015,8 +1015,9 @@ static void mos7720_break(struct tty_struct *tty, int break_state) data = mos7720_port->shadowLCR & ~UART_LCR_SBC; mos7720_port->shadowLCR = data; - write_mos_reg(serial, port->port_number, MOS7720_LCR, - mos7720_port->shadowLCR); + + return write_mos_reg(serial, port->port_number, MOS7720_LCR, + mos7720_port->shadowLCR); } /* diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index 73370eaae0ab..8b0308d84270 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -787,7 +787,7 @@ static void mos7840_close(struct usb_serial_port *port) * mos7840_break * this function sends a break to the port *****************************************************************************/ -static void mos7840_break(struct tty_struct *tty, int break_state) +static int mos7840_break(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; struct moschip_port *mos7840_port = usb_get_serial_port_data(port); @@ -801,8 +801,9 @@ static void mos7840_break(struct tty_struct *tty, int break_state) /* FIXME: no locking on shadowLCR anywhere in driver */ mos7840_port->shadowLCR = data; dev_dbg(&port->dev, "%s mos7840_port->shadowLCR is %x\n", __func__, mos7840_port->shadowLCR); - mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, - mos7840_port->shadowLCR); + + return mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, + mos7840_port->shadowLCR); } /***************************************************************************** diff --git a/drivers/usb/serial/mxuport.c b/drivers/usb/serial/mxuport.c index faa0eedfe245..1f7bb3e4fcf2 100644 --- a/drivers/usb/serial/mxuport.c +++ b/drivers/usb/serial/mxuport.c @@ -1230,7 +1230,7 @@ static void mxuport_close(struct usb_serial_port *port) } /* Send a break to the port. */ -static void mxuport_break_ctl(struct tty_struct *tty, int break_state) +static int mxuport_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; struct usb_serial *serial = port->serial; @@ -1244,8 +1244,8 @@ static void mxuport_break_ctl(struct tty_struct *tty, int break_state) dev_dbg(&port->dev, "%s - clearing break\n", __func__); } - mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_BREAK, - enable, port->port_number); + return mxuport_send_ctrl_urb(serial, RQ_VENDOR_SET_BREAK, + enable, port->port_number); } static int mxuport_resume(struct usb_serial *serial) diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 8949c1891164..d93f5d584557 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -173,7 +173,7 @@ MODULE_DEVICE_TABLE(usb, id_table); #define PL2303_HXN_FLOWCTRL_RTS_CTS 0x18 #define PL2303_HXN_FLOWCTRL_XON_XOFF 0x0c -static void pl2303_set_break(struct usb_serial_port *port, bool enable); +static int pl2303_set_break(struct usb_serial_port *port, bool enable); enum pl2303_type { TYPE_H, @@ -1060,7 +1060,7 @@ static int pl2303_carrier_raised(struct usb_serial_port *port) return 0; } -static void pl2303_set_break(struct usb_serial_port *port, bool enable) +static int pl2303_set_break(struct usb_serial_port *port, bool enable) { struct usb_serial *serial = port->serial; u16 state; @@ -1077,15 +1077,19 @@ static void pl2303_set_break(struct usb_serial_port *port, bool enable) result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), BREAK_REQUEST, BREAK_REQUEST_TYPE, state, 0, NULL, 0, 100); - if (result) + if (result) { dev_err(&port->dev, "error sending break = %d\n", result); + return result; + } + + return 0; } -static void pl2303_break_ctl(struct tty_struct *tty, int state) +static int pl2303_break_ctl(struct tty_struct *tty, int state) { struct usb_serial_port *port = tty->driver_data; - pl2303_set_break(port, state); + return pl2303_set_break(port, state); } static void pl2303_update_line_status(struct usb_serial_port *port, diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c index fee581409bf6..821f25e52ec2 100644 --- a/drivers/usb/serial/quatech2.c +++ b/drivers/usb/serial/quatech2.c @@ -741,7 +741,7 @@ static int qt2_tiocmset(struct tty_struct *tty, return update_mctrl(port_priv, set, clear); } -static void qt2_break_ctl(struct tty_struct *tty, int break_state) +static int qt2_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; struct qt2_port_private *port_priv; @@ -754,10 +754,14 @@ static void qt2_break_ctl(struct tty_struct *tty, int break_state) status = qt2_control_msg(port->serial->dev, QT2_BREAK_CONTROL, val, port_priv->device_port); - if (status < 0) + if (status < 0) { dev_warn(&port->dev, "%s - failed to send control message: %i\n", __func__, status); + return status; + } + + return 0; } diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c index b99f78224846..0fba25abf671 100644 --- a/drivers/usb/serial/ti_usb_3410_5052.c +++ b/drivers/usb/serial/ti_usb_3410_5052.c @@ -319,7 +319,7 @@ static void ti_set_termios(struct tty_struct *tty, static int ti_tiocmget(struct tty_struct *tty); static int ti_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); -static void ti_break(struct tty_struct *tty, int break_state); +static int ti_break(struct tty_struct *tty, int break_state); static void ti_interrupt_callback(struct urb *urb); static void ti_bulk_in_callback(struct urb *urb); static void ti_bulk_out_callback(struct urb *urb); @@ -1071,7 +1071,7 @@ static int ti_tiocmset(struct tty_struct *tty, } -static void ti_break(struct tty_struct *tty, int break_state) +static int ti_break(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; struct ti_port *tport = usb_get_serial_port_data(port); @@ -1083,8 +1083,12 @@ static void ti_break(struct tty_struct *tty, int break_state) tport->tp_uart_base_addr + TI_UART_OFFSET_LCR, TI_LCR_BREAK, break_state == -1 ? TI_LCR_BREAK : 0); - if (status) + if (status) { dev_dbg(&port->dev, "%s - error setting break, %d\n", __func__, status); + return status; + } + + return 0; } static int ti_get_port_from_code(unsigned char code) diff --git a/drivers/usb/serial/upd78f0730.c b/drivers/usb/serial/upd78f0730.c index c47439bd90fa..46952182e04f 100644 --- a/drivers/usb/serial/upd78f0730.c +++ b/drivers/usb/serial/upd78f0730.c @@ -238,12 +238,13 @@ static int upd78f0730_tiocmset(struct tty_struct *tty, return res; } -static void upd78f0730_break_ctl(struct tty_struct *tty, int break_state) +static int upd78f0730_break_ctl(struct tty_struct *tty, int break_state) { struct upd78f0730_port_private *private; struct usb_serial_port *port = tty->driver_data; struct upd78f0730_set_dtr_rts request; struct device *dev = &port->dev; + int res; private = usb_get_serial_port_data(port); @@ -258,8 +259,10 @@ static void upd78f0730_break_ctl(struct tty_struct *tty, int break_state) request.opcode = UPD78F0730_CMD_SET_DTR_RTS; request.params = private->line_signals; - upd78f0730_send_ctl(port, &request, sizeof(request)); + res = upd78f0730_send_ctl(port, &request, sizeof(request)); mutex_unlock(&private->lock); + + return res; } static void upd78f0730_dtr_rts(struct usb_serial_port *port, int on) diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index f8404073558b..470634444af7 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -539,7 +539,7 @@ static int serial_break(struct tty_struct *tty, int break_state) dev_dbg(&port->dev, "%s\n", __func__); if (port->serial->type->break_ctl) - port->serial->type->break_ctl(tty, break_state); + return port->serial->type->break_ctl(tty, break_state); return 0; } diff --git a/drivers/usb/serial/usb_debug.c b/drivers/usb/serial/usb_debug.c index aaf4813e4971..6934970f180d 100644 --- a/drivers/usb/serial/usb_debug.c +++ b/drivers/usb/serial/usb_debug.c @@ -47,12 +47,19 @@ MODULE_DEVICE_TABLE(usb, id_table_combined); /* This HW really does not support a serial break, so one will be * emulated when ever the break state is set to true. */ -static void usb_debug_break_ctl(struct tty_struct *tty, int break_state) +static int usb_debug_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; + int ret; + if (!break_state) - return; - usb_serial_generic_write(tty, port, USB_DEBUG_BRK, USB_DEBUG_BRK_SIZE); + return 0; + + ret = usb_serial_generic_write(tty, port, USB_DEBUG_BRK, USB_DEBUG_BRK_SIZE); + if (ret < 0) + return ret; + + return 0; } static void usb_debug_process_read_urb(struct urb *urb) diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index 7f82d40753ee..ca48e90a8e81 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -87,7 +87,7 @@ static void whiteheat_set_termios(struct tty_struct *tty, static int whiteheat_tiocmget(struct tty_struct *tty); static int whiteheat_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear); -static void whiteheat_break_ctl(struct tty_struct *tty, int break_state); +static int whiteheat_break_ctl(struct tty_struct *tty, int break_state); static struct usb_serial_driver whiteheat_fake_device = { .driver = { @@ -449,10 +449,11 @@ static void whiteheat_set_termios(struct tty_struct *tty, firm_setup_port(tty); } -static void whiteheat_break_ctl(struct tty_struct *tty, int break_state) +static int whiteheat_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; - firm_set_break(port, break_state); + + return firm_set_break(port, break_state); } diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c index fdb0aae546c3..4ec7c5892b84 100644 --- a/drivers/usb/serial/xr_serial.c +++ b/drivers/usb/serial/xr_serial.c @@ -503,7 +503,7 @@ static void xr_dtr_rts(struct usb_serial_port *port, int on) xr_tiocmset_port(port, 0, TIOCM_DTR | TIOCM_RTS); } -static void xr_break_ctl(struct tty_struct *tty, int break_state) +static int xr_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; struct xr_data *data = usb_get_serial_port_data(port); @@ -517,7 +517,7 @@ static void xr_break_ctl(struct tty_struct *tty, int break_state) dev_dbg(&port->dev, "Turning break %s\n", state == 0 ? "off" : "on"); - xr_set_reg_uart(port, type->tx_break, state); + return xr_set_reg_uart(port, type->tx_break, state); } /* Tx and Rx clock mask values obtained from section 3.3.4 of datasheet */ diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 7eeb5f9c4f0d..1a0a4dc87980 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -278,7 +278,7 @@ struct usb_serial_driver { int (*set_serial)(struct tty_struct *tty, struct serial_struct *ss); void (*set_termios)(struct tty_struct *tty, struct usb_serial_port *port, const struct ktermios *old); - void (*break_ctl)(struct tty_struct *tty, int break_state); + int (*break_ctl)(struct tty_struct *tty, int break_state); unsigned int (*chars_in_buffer)(struct tty_struct *tty); void (*wait_until_sent)(struct tty_struct *tty, long timeout); bool (*tx_empty)(struct usb_serial_port *port); -- cgit v1.2.3 From f4bbae27b32ea0ffb9e25931b9769600e8d8a664 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sun, 4 Jun 2023 14:35:04 +0200 Subject: USB: serial: cp210x: disable break signalling on CP2105 SCI Only the first UART interface (ECI) on CP2105 supports break signalling. Return an error on requests for break state changes for the second interface (SCI) to avoid transmitting a garbage character and waiting when break is not supported. Tested-by: Corey Minyard Signed-off-by: Johan Hovold --- drivers/usb/serial/cp210x.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c index 81e49ed9d147..1e61fe043171 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -1437,8 +1437,14 @@ static int cp210x_tiocmget(struct tty_struct *tty) static int cp210x_break_ctl(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; + struct cp210x_serial_private *priv = usb_get_serial_data(port->serial); u16 state; + if (priv->partnum == CP210X_PARTNUM_CP2105) { + if (cp210x_interface_num(port->serial) == 1) + return -ENOTTY; + } + if (break_state == 0) state = BREAK_OFF; else -- cgit v1.2.3 From c9d934053d9e850bc901b6425eacb5fe3d4b1738 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Sun, 4 Jun 2023 14:35:05 +0200 Subject: USB: serial: report unsupported break signalling Instead of returning success when a driver does not support break signalling, return an error to let user space know and to avoid waiting when break is not supported. Tested-by: Corey Minyard Signed-off-by: Johan Hovold --- drivers/usb/serial/usb-serial.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 470634444af7..7b4805c1004d 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -541,7 +541,7 @@ static int serial_break(struct tty_struct *tty, int break_state) if (port->serial->type->break_ctl) return port->serial->type->break_ctl(tty, break_state); - return 0; + return -ENOTTY; } static int serial_proc_show(struct seq_file *m, void *v) -- cgit v1.2.3 From 7ce542219b6323764cb922722b677005404d9b60 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Fri, 23 Sep 2022 01:30:38 +0300 Subject: thunderbolt: Introduce tb_switch_downstream_port() Introduce tb_switch_downstream_port() helper function that returns the downstream port of a parent switch that is connected to the upstream port of specified switch. From now on, we use it all across the driver where applicable. While there fix a whitespace in comment and rename 'downstream' to 'down' to be consistent with the rest of the driver. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/acpi.c | 5 ++--- drivers/thunderbolt/icm.c | 24 ++++++++++-------------- drivers/thunderbolt/switch.c | 19 +++++++------------ drivers/thunderbolt/tb.c | 8 +++----- drivers/thunderbolt/tb.h | 14 ++++++++++++++ drivers/thunderbolt/tmu.c | 29 +++++++++++++---------------- drivers/thunderbolt/usb4.c | 9 ++++----- 7 files changed, 53 insertions(+), 55 deletions(-) diff --git a/drivers/thunderbolt/acpi.c b/drivers/thunderbolt/acpi.c index 3514bf65b7a4..38fefd0e5268 100644 --- a/drivers/thunderbolt/acpi.c +++ b/drivers/thunderbolt/acpi.c @@ -296,16 +296,15 @@ static bool tb_acpi_bus_match(struct device *dev) static struct acpi_device *tb_acpi_switch_find_companion(struct tb_switch *sw) { + struct tb_switch *parent_sw = tb_switch_parent(sw); struct acpi_device *adev = NULL; - struct tb_switch *parent_sw; /* * Device routers exists under the downstream facing USB4 port * of the parent router. Their _ADR is always 0. */ - parent_sw = tb_switch_parent(sw); if (parent_sw) { - struct tb_port *port = tb_port_at(tb_route(sw), parent_sw); + struct tb_port *port = tb_switch_downstream_port(sw); struct acpi_device *port_adev; port_adev = acpi_find_child_by_adr(ACPI_COMPANION(&parent_sw->dev), diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index 86521ebb2579..05274caf1466 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -644,13 +644,14 @@ static int add_switch(struct tb_switch *parent_sw, struct tb_switch *sw) return ret; } -static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw, - u64 route, u8 connection_id, u8 connection_key, - u8 link, u8 depth, bool boot) +static void update_switch(struct tb_switch *sw, u64 route, u8 connection_id, + u8 connection_key, u8 link, u8 depth, bool boot) { + struct tb_switch *parent_sw = tb_switch_parent(sw); + /* Disconnect from parent */ - tb_port_at(tb_route(sw), parent_sw)->remote = NULL; - /* Re-connect via updated port*/ + tb_switch_downstream_port(sw)->remote = NULL; + /* Re-connect via updated port */ tb_port_at(route, parent_sw)->remote = tb_upstream_port(sw); /* Update with the new addressing information */ @@ -671,10 +672,7 @@ static void update_switch(struct tb_switch *parent_sw, struct tb_switch *sw, static void remove_switch(struct tb_switch *sw) { - struct tb_switch *parent_sw; - - parent_sw = tb_to_switch(sw->dev.parent); - tb_port_at(tb_route(sw), parent_sw)->remote = NULL; + tb_switch_downstream_port(sw)->remote = NULL; tb_switch_remove(sw); } @@ -755,7 +753,6 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) if (sw) { u8 phy_port, sw_phy_port; - parent_sw = tb_to_switch(sw->dev.parent); sw_phy_port = tb_phy_port_from_link(sw->link); phy_port = tb_phy_port_from_link(link); @@ -785,7 +782,7 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) route = tb_route(sw); } - update_switch(parent_sw, sw, route, pkg->connection_id, + update_switch(sw, route, pkg->connection_id, pkg->connection_key, link, depth, boot); tb_switch_put(sw); return; @@ -1236,9 +1233,8 @@ __icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr, if (sw) { /* Update the switch if it is still in the same place */ if (tb_route(sw) == route && !!sw->authorized == authorized) { - parent_sw = tb_to_switch(sw->dev.parent); - update_switch(parent_sw, sw, route, pkg->connection_id, - 0, 0, 0, boot); + update_switch(sw, route, pkg->connection_id, 0, 0, 0, + boot); tb_switch_put(sw); return; } diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 51e86b5171c7..4f3d02c58c9e 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -2754,7 +2754,6 @@ static int tb_switch_update_link_attributes(struct tb_switch *sw) */ int tb_switch_lane_bonding_enable(struct tb_switch *sw) { - struct tb_switch *parent = tb_to_switch(sw->dev.parent); struct tb_port *up, *down; u64 route = tb_route(sw); int ret; @@ -2766,7 +2765,7 @@ int tb_switch_lane_bonding_enable(struct tb_switch *sw) return 0; up = tb_upstream_port(sw); - down = tb_port_at(route, parent); + down = tb_switch_downstream_port(sw); if (!tb_port_is_width_supported(up, 2) || !tb_port_is_width_supported(down, 2)) @@ -2808,7 +2807,6 @@ int tb_switch_lane_bonding_enable(struct tb_switch *sw) */ void tb_switch_lane_bonding_disable(struct tb_switch *sw) { - struct tb_switch *parent = tb_to_switch(sw->dev.parent); struct tb_port *up, *down; if (!tb_route(sw)) @@ -2818,7 +2816,7 @@ void tb_switch_lane_bonding_disable(struct tb_switch *sw) if (!up->bonded) return; - down = tb_port_at(tb_route(sw), parent); + down = tb_switch_downstream_port(sw); tb_port_lane_bonding_disable(up); tb_port_lane_bonding_disable(down); @@ -3476,7 +3474,6 @@ struct tb_port *tb_switch_find_port(struct tb_switch *sw, static int tb_switch_pm_secondary_resolve(struct tb_switch *sw) { - struct tb_switch *parent = tb_switch_parent(sw); struct tb_port *up, *down; int ret; @@ -3484,7 +3481,7 @@ static int tb_switch_pm_secondary_resolve(struct tb_switch *sw) return 0; up = tb_upstream_port(sw); - down = tb_port_at(tb_route(sw), parent); + down = tb_switch_downstream_port(sw); ret = tb_port_pm_secondary_enable(up); if (ret) return ret; @@ -3494,7 +3491,6 @@ static int tb_switch_pm_secondary_resolve(struct tb_switch *sw) static int __tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx) { - struct tb_switch *parent = tb_switch_parent(sw); bool up_clx_support, down_clx_support; struct tb_port *up, *down; int ret; @@ -3510,7 +3506,7 @@ static int __tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx) return 0; /* Enable CLx only for first hop router (depth = 1) */ - if (tb_route(parent)) + if (tb_route(tb_switch_parent(sw))) return 0; ret = tb_switch_pm_secondary_resolve(sw); @@ -3518,7 +3514,7 @@ static int __tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx) return ret; up = tb_upstream_port(sw); - down = tb_port_at(tb_route(sw), parent); + down = tb_switch_downstream_port(sw); up_clx_support = tb_port_clx_supported(up, clx); down_clx_support = tb_port_clx_supported(down, clx); @@ -3594,7 +3590,6 @@ int tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx) static int __tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx) { - struct tb_switch *parent = tb_switch_parent(sw); struct tb_port *up, *down; int ret; @@ -3609,11 +3604,11 @@ static int __tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx) return 0; /* Disable CLx only for first hop router (depth = 1) */ - if (tb_route(parent)) + if (tb_route(tb_switch_parent(sw))) return 0; up = tb_upstream_port(sw); - down = tb_port_at(tb_route(sw), parent); + down = tb_switch_downstream_port(sw); ret = tb_port_clx_disable(up, clx); if (ret) return ret; diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index c1af712ca728..1ab3aa114a17 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -628,7 +628,7 @@ static int tb_tunnel_usb3(struct tb *tb, struct tb_switch *sw) * Look up available down port. Since we are chaining it should * be found right above this switch. */ - port = tb_port_at(tb_route(sw), parent); + port = tb_switch_downstream_port(sw); down = tb_find_usb3_down(parent, port); if (!down) return 0; @@ -1378,7 +1378,6 @@ static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw) { struct tb_port *up, *down, *port; struct tb_cm *tcm = tb_priv(tb); - struct tb_switch *parent_sw; struct tb_tunnel *tunnel; up = tb_switch_find_port(sw, TB_TYPE_PCIE_UP); @@ -1389,9 +1388,8 @@ static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw) * Look up available down port. Since we are chaining it should * be found right above this switch. */ - parent_sw = tb_to_switch(sw->dev.parent); - port = tb_port_at(tb_route(sw), parent_sw); - down = tb_find_pcie_down(parent_sw, port); + port = tb_switch_downstream_port(sw); + down = tb_find_pcie_down(tb_switch_parent(sw), port); if (!down) return 0; diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 275ff5219a3a..a8e1725b4cb5 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -857,6 +857,20 @@ static inline struct tb_switch *tb_switch_parent(struct tb_switch *sw) return tb_to_switch(sw->dev.parent); } +/** + * tb_switch_downstream_port() - Return downstream facing port of parent router + * @sw: Device router pointer + * + * Only call for device routers. Returns the downstream facing port of + * the parent router. + */ +static inline struct tb_port *tb_switch_downstream_port(struct tb_switch *sw) +{ + if (WARN_ON(!tb_route(sw))) + return NULL; + return tb_port_at(tb_route(sw), tb_switch_parent(sw)); +} + static inline bool tb_switch_is_light_ridge(const struct tb_switch *sw) { return sw->config.vendor_id == PCI_VENDOR_ID_INTEL && diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index 626aca3124b1..a46203b33c5f 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -398,11 +398,10 @@ int tb_switch_tmu_disable(struct tb_switch *sw) if (tb_route(sw)) { bool unidirectional = sw->tmu.unidirectional; - struct tb_switch *parent = tb_switch_parent(sw); struct tb_port *down, *up; int ret; - down = tb_port_at(tb_route(sw), parent); + down = tb_switch_downstream_port(sw); up = tb_upstream_port(sw); /* * In case of uni-directional time sync, TMU handshake is @@ -442,10 +441,9 @@ int tb_switch_tmu_disable(struct tb_switch *sw) static void __tb_switch_tmu_off(struct tb_switch *sw, bool unidirectional) { - struct tb_switch *parent = tb_switch_parent(sw); struct tb_port *down, *up; - down = tb_port_at(tb_route(sw), parent); + down = tb_switch_downstream_port(sw); up = tb_upstream_port(sw); /* * In case of any failure in one of the steps when setting @@ -457,7 +455,8 @@ static void __tb_switch_tmu_off(struct tb_switch *sw, bool unidirectional) tb_port_tmu_time_sync_disable(down); tb_port_tmu_time_sync_disable(up); if (unidirectional) - tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_OFF); + tb_switch_tmu_rate_write(tb_switch_parent(sw), + TB_SWITCH_TMU_RATE_OFF); else tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF); @@ -472,12 +471,11 @@ static void __tb_switch_tmu_off(struct tb_switch *sw, bool unidirectional) */ static int __tb_switch_tmu_enable_bidirectional(struct tb_switch *sw) { - struct tb_switch *parent = tb_switch_parent(sw); struct tb_port *up, *down; int ret; up = tb_upstream_port(sw); - down = tb_port_at(tb_route(sw), parent); + down = tb_switch_downstream_port(sw); ret = tb_port_tmu_unidirectional_disable(up); if (ret) @@ -537,13 +535,13 @@ static int tb_switch_tmu_unidirectional_enable(struct tb_switch *sw) */ static int __tb_switch_tmu_enable_unidirectional(struct tb_switch *sw) { - struct tb_switch *parent = tb_switch_parent(sw); struct tb_port *up, *down; int ret; up = tb_upstream_port(sw); - down = tb_port_at(tb_route(sw), parent); - ret = tb_switch_tmu_rate_write(parent, sw->tmu.rate_request); + down = tb_switch_downstream_port(sw); + ret = tb_switch_tmu_rate_write(tb_switch_parent(sw), + sw->tmu.rate_request); if (ret) return ret; @@ -576,10 +574,9 @@ out: static void __tb_switch_tmu_change_mode_prev(struct tb_switch *sw) { - struct tb_switch *parent = tb_switch_parent(sw); struct tb_port *down, *up; - down = tb_port_at(tb_route(sw), parent); + down = tb_switch_downstream_port(sw); up = tb_upstream_port(sw); /* * In case of any failure in one of the steps when change mode, @@ -589,7 +586,7 @@ static void __tb_switch_tmu_change_mode_prev(struct tb_switch *sw) */ tb_port_tmu_set_unidirectional(down, sw->tmu.unidirectional); if (sw->tmu.unidirectional_request) - tb_switch_tmu_rate_write(parent, sw->tmu.rate); + tb_switch_tmu_rate_write(tb_switch_parent(sw), sw->tmu.rate); else tb_switch_tmu_rate_write(sw, sw->tmu.rate); @@ -599,18 +596,18 @@ static void __tb_switch_tmu_change_mode_prev(struct tb_switch *sw) static int __tb_switch_tmu_change_mode(struct tb_switch *sw) { - struct tb_switch *parent = tb_switch_parent(sw); struct tb_port *up, *down; int ret; up = tb_upstream_port(sw); - down = tb_port_at(tb_route(sw), parent); + down = tb_switch_downstream_port(sw); ret = tb_port_tmu_set_unidirectional(down, sw->tmu.unidirectional_request); if (ret) goto out; if (sw->tmu.unidirectional_request) - ret = tb_switch_tmu_rate_write(parent, sw->tmu.rate_request); + ret = tb_switch_tmu_rate_write(tb_switch_parent(sw), + sw->tmu.rate_request); else ret = tb_switch_tmu_rate_write(sw, sw->tmu.rate_request); if (ret) diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 485b6e430686..9f5a98347bee 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -234,8 +234,8 @@ static bool link_is_usb4(struct tb_port *port) */ int usb4_switch_setup(struct tb_switch *sw) { - struct tb_port *downstream_port; - struct tb_switch *parent; + struct tb_switch *parent = tb_switch_parent(sw); + struct tb_port *down; bool tbt3, xhci; u32 val = 0; int ret; @@ -249,9 +249,8 @@ int usb4_switch_setup(struct tb_switch *sw) if (ret) return ret; - parent = tb_switch_parent(sw); - downstream_port = tb_port_at(tb_route(sw), parent); - sw->link_usb4 = link_is_usb4(downstream_port); + down = tb_switch_downstream_port(sw); + sw->link_usb4 = link_is_usb4(down); tb_sw_dbg(sw, "link: %s\n", sw->link_usb4 ? "USB4" : "TBT"); xhci = val & ROUTER_CS_6_HCI; -- cgit v1.2.3 From 17fb1a3df9c3059798bfd823f58407e206c31ab9 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 30 Sep 2022 11:12:42 +0300 Subject: thunderbolt: Introduce tb_xdomain_downstream_port() In the same way we did for the routers add a function that returns the parent routers downstream facing port for XDomain devices. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.h | 11 +++++++++++ drivers/thunderbolt/xdomain.c | 16 +++++++--------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index a8e1725b4cb5..1e617c6e11ae 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1197,6 +1197,17 @@ static inline struct tb_switch *tb_xdomain_parent(struct tb_xdomain *xd) return tb_to_switch(xd->dev.parent); } +/** + * tb_xdomain_downstream_port() - Return downstream facing port of parent router + * @xd: Xdomain pointer + * + * Returns the downstream port the XDomain is connected to. + */ +static inline struct tb_port *tb_xdomain_downstream_port(struct tb_xdomain *xd) +{ + return tb_port_at(xd->route, tb_xdomain_parent(xd)); +} + int tb_retimer_nvm_read(struct tb_retimer *rt, unsigned int address, void *buf, size_t size); int tb_retimer_scan(struct tb_port *port, bool add); diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index e2b54887d331..8389961b2d45 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -537,9 +537,8 @@ static int tb_xdp_link_state_status_request(struct tb_ctl *ctl, u64 route, static int tb_xdp_link_state_status_response(struct tb *tb, struct tb_ctl *ctl, struct tb_xdomain *xd, u8 sequence) { - struct tb_switch *sw = tb_to_switch(xd->dev.parent); struct tb_xdp_link_state_status_response res; - struct tb_port *port = tb_port_at(xd->route, sw); + struct tb_port *port = tb_xdomain_downstream_port(xd); u32 val[2]; int ret; @@ -1137,7 +1136,7 @@ static int tb_xdomain_update_link_attributes(struct tb_xdomain *xd) struct tb_port *port; int ret; - port = tb_port_at(xd->route, tb_xdomain_parent(xd)); + port = tb_xdomain_downstream_port(xd); ret = tb_port_get_link_speed(port); if (ret < 0) @@ -1251,8 +1250,7 @@ static int tb_xdomain_get_link_status(struct tb_xdomain *xd) static int tb_xdomain_link_state_change(struct tb_xdomain *xd, unsigned int width) { - struct tb_switch *sw = tb_to_switch(xd->dev.parent); - struct tb_port *port = tb_port_at(xd->route, sw); + struct tb_port *port = tb_xdomain_downstream_port(xd); struct tb *tb = xd->tb; u8 tlw, tls; u32 val; @@ -1309,7 +1307,7 @@ static int tb_xdomain_bond_lanes_uuid_high(struct tb_xdomain *xd) return -ETIMEDOUT; } - port = tb_port_at(xd->route, tb_xdomain_parent(xd)); + port = tb_xdomain_downstream_port(xd); /* * We can't use tb_xdomain_lane_bonding_enable() here because it @@ -1425,7 +1423,7 @@ static int tb_xdomain_get_properties(struct tb_xdomain *xd) if (xd->bonding_possible) { struct tb_port *port; - port = tb_port_at(xd->route, tb_xdomain_parent(xd)); + port = tb_xdomain_downstream_port(xd); if (!port->bonded) tb_port_disable(port->dual_link_port); } @@ -1979,7 +1977,7 @@ int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd) struct tb_port *port; int ret; - port = tb_port_at(xd->route, tb_xdomain_parent(xd)); + port = tb_xdomain_downstream_port(xd); if (!port->dual_link_port) return -ENODEV; @@ -2024,7 +2022,7 @@ void tb_xdomain_lane_bonding_disable(struct tb_xdomain *xd) { struct tb_port *port; - port = tb_port_at(xd->route, tb_xdomain_parent(xd)); + port = tb_xdomain_downstream_port(xd); if (port->dual_link_port) { tb_port_lane_bonding_disable(port); if (tb_port_wait_for_link_width(port, 1, 100) == -ETIMEDOUT) -- cgit v1.2.3 From c437dcb18310f296eb9db58a361f309f7817014d Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 7 Oct 2022 13:14:13 +0300 Subject: thunderbolt: Fix a couple of style issues in TMU code Drop extra empty line and get rid of the '__' in function names. No functional changes. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tmu.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index a46203b33c5f..8614e154be5f 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -395,7 +395,6 @@ int tb_switch_tmu_disable(struct tb_switch *sw) if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF) return 0; - if (tb_route(sw)) { bool unidirectional = sw->tmu.unidirectional; struct tb_port *down, *up; @@ -439,7 +438,7 @@ int tb_switch_tmu_disable(struct tb_switch *sw) return 0; } -static void __tb_switch_tmu_off(struct tb_switch *sw, bool unidirectional) +static void tb_switch_tmu_off(struct tb_switch *sw, bool unidirectional) { struct tb_port *down, *up; @@ -469,7 +468,7 @@ static void __tb_switch_tmu_off(struct tb_switch *sw, bool unidirectional) * This function is called when the previous TMU mode was * TB_SWITCH_TMU_RATE_OFF. */ -static int __tb_switch_tmu_enable_bidirectional(struct tb_switch *sw) +static int tb_switch_tmu_enable_bidirectional(struct tb_switch *sw) { struct tb_port *up, *down; int ret; @@ -500,7 +499,7 @@ static int __tb_switch_tmu_enable_bidirectional(struct tb_switch *sw) return 0; out: - __tb_switch_tmu_off(sw, false); + tb_switch_tmu_off(sw, false); return ret; } @@ -533,7 +532,7 @@ static int tb_switch_tmu_unidirectional_enable(struct tb_switch *sw) * This function is called when the previous TMU mode was * TB_SWITCH_TMU_RATE_OFF. */ -static int __tb_switch_tmu_enable_unidirectional(struct tb_switch *sw) +static int tb_switch_tmu_enable_unidirectional(struct tb_switch *sw) { struct tb_port *up, *down; int ret; @@ -568,11 +567,11 @@ static int __tb_switch_tmu_enable_unidirectional(struct tb_switch *sw) return 0; out: - __tb_switch_tmu_off(sw, true); + tb_switch_tmu_off(sw, true); return ret; } -static void __tb_switch_tmu_change_mode_prev(struct tb_switch *sw) +static void tb_switch_tmu_change_mode_prev(struct tb_switch *sw) { struct tb_port *down, *up; @@ -594,7 +593,7 @@ static void __tb_switch_tmu_change_mode_prev(struct tb_switch *sw) tb_port_tmu_set_unidirectional(up, sw->tmu.unidirectional); } -static int __tb_switch_tmu_change_mode(struct tb_switch *sw) +static int tb_switch_tmu_change_mode(struct tb_switch *sw) { struct tb_port *up, *down; int ret; @@ -632,7 +631,7 @@ static int __tb_switch_tmu_change_mode(struct tb_switch *sw) return 0; out: - __tb_switch_tmu_change_mode_prev(sw); + tb_switch_tmu_change_mode_prev(sw); return ret; } @@ -695,13 +694,13 @@ int tb_switch_tmu_enable(struct tb_switch *sw) */ if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF) { if (unidirectional) - ret = __tb_switch_tmu_enable_unidirectional(sw); + ret = tb_switch_tmu_enable_unidirectional(sw); else - ret = __tb_switch_tmu_enable_bidirectional(sw); + ret = tb_switch_tmu_enable_bidirectional(sw); if (ret) return ret; } else if (sw->tmu.rate == TB_SWITCH_TMU_RATE_NORMAL) { - ret = __tb_switch_tmu_change_mode(sw); + ret = tb_switch_tmu_change_mode(sw); if (ret) return ret; } -- cgit v1.2.3 From 826f55d50de95572661ab4508d6aa0649a60627b Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 7 Oct 2022 13:19:09 +0300 Subject: thunderbolt: Drop useless 'unidirectional' parameter from tb_switch_tmu_is_enabled() There is no point passing it as we already have a field for that. While there clean up the kernel-doc of things that do not really belong to the API documentation (these can be figured out from the spec itself). No functional changes. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 2 +- drivers/thunderbolt/tb.h | 10 ++++------ drivers/thunderbolt/tmu.c | 11 ++++------- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 1ab3aa114a17..72041e29e544 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -362,7 +362,7 @@ static int tb_enable_tmu(struct tb_switch *sw) int ret; /* If it is already enabled in correct mode, don't touch it */ - if (tb_switch_tmu_is_enabled(sw, sw->tmu.unidirectional_request)) + if (tb_switch_tmu_is_enabled(sw)) return 0; ret = tb_switch_tmu_disable(sw); diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 1e617c6e11ae..75dbe00d7cf6 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -995,16 +995,14 @@ void tb_switch_enable_tmu_1st_child(struct tb_switch *sw, /** * tb_switch_tmu_is_enabled() - Checks if the specified TMU mode is enabled * @sw: Router whose TMU mode to check - * @unidirectional: If uni-directional (bi-directional otherwise) * - * Return true if hardware TMU configuration matches the one passed in - * as parameter. That is HiFi/Normal and either uni-directional or bi-directional. + * Return true if hardware TMU configuration matches the requested + * configuration. */ -static inline bool tb_switch_tmu_is_enabled(const struct tb_switch *sw, - bool unidirectional) +static inline bool tb_switch_tmu_is_enabled(const struct tb_switch *sw) { return sw->tmu.rate == sw->tmu.rate_request && - sw->tmu.unidirectional == unidirectional; + sw->tmu.unidirectional == sw->tmu.unidirectional_request; } static inline const char *tb_switch_clx_name(enum tb_clx clx) diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index 8614e154be5f..5d508ea8baa5 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -639,12 +639,9 @@ out: * tb_switch_tmu_enable() - Enable TMU on a router * @sw: Router whose TMU to enable * - * Enables TMU of a router to be in uni-directional Normal/HiFi - * or bi-directional HiFi mode. Calling tb_switch_tmu_configure() is required - * before calling this function, to select the mode Normal/HiFi and - * directionality (uni-directional/bi-directional). - * In HiFi mode all tunneling should work. In Normal mode, DP tunneling can't - * work. Uni-directional mode is required for CLx (Link Low-Power) to work. + * Enables TMU of a router to be in uni-directional Normal/HiFi or + * bi-directional HiFi mode. Calling tb_switch_tmu_configure() is + * required before calling this function. */ int tb_switch_tmu_enable(struct tb_switch *sw) { @@ -662,7 +659,7 @@ int tb_switch_tmu_enable(struct tb_switch *sw) if (!tb_switch_is_clx_supported(sw)) return 0; - if (tb_switch_tmu_is_enabled(sw, sw->tmu.unidirectional_request)) + if (tb_switch_tmu_is_enabled(sw)) return 0; if (tb_switch_is_titan_ridge(sw) && unidirectional) { -- cgit v1.2.3 From 701e73a823bb887be1aabebf1ffaba068d329550 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 26 May 2023 09:22:06 +0300 Subject: thunderbolt: Rework Titan Ridge TMU objection disable function Now this is split into two with one having a misleading name (tb_switch_tmu_unidirectional_enable()). Make this easier to read, rename and consolidate the two functions into one with name that explains what it actually does. Use the two constants as well that were added but never used to make it clear which bits are being set. No functional changes. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tmu.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index 5d508ea8baa5..30f18806abb7 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -503,8 +503,10 @@ out: return ret; } -static int tb_switch_tmu_objection_mask(struct tb_switch *sw) +/* Only needed for Titan Ridge */ +static int tb_switch_tmu_disable_objections(struct tb_switch *sw) { + struct tb_port *up = tb_upstream_port(sw); u32 val; int ret; @@ -515,17 +517,15 @@ static int tb_switch_tmu_objection_mask(struct tb_switch *sw) val &= ~TB_TIME_VSEC_3_CS_9_TMU_OBJ_MASK; - return tb_sw_write(sw, &val, TB_CFG_SWITCH, - sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_9, 1); -} - -static int tb_switch_tmu_unidirectional_enable(struct tb_switch *sw) -{ - struct tb_port *up = tb_upstream_port(sw); + ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, + sw->cap_vsec_tmu + TB_TIME_VSEC_3_CS_9, 1); + if (ret) + return ret; return tb_port_tmu_write(up, TMU_ADP_CS_6, TMU_ADP_CS_6_DISABLE_TMU_OBJ_MASK, - TMU_ADP_CS_6_DISABLE_TMU_OBJ_MASK); + TMU_ADP_CS_6_DISABLE_TMU_OBJ_CL1 | + TMU_ADP_CS_6_DISABLE_TMU_OBJ_CL2); } /* @@ -670,11 +670,7 @@ int tb_switch_tmu_enable(struct tb_switch *sw) if (!tb_switch_is_clx_enabled(sw, TB_CL1)) return -EOPNOTSUPP; - ret = tb_switch_tmu_objection_mask(sw); - if (ret) - return ret; - - ret = tb_switch_tmu_unidirectional_enable(sw); + ret = tb_switch_tmu_disable_objections(sw); if (ret) return ret; } -- cgit v1.2.3 From 7d283f4148f184b200854acdc21308e5bfee9fc1 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 7 Oct 2022 15:40:21 +0300 Subject: thunderbolt: Get rid of tb_switch_enable_tmu_1st_child() This is better to be part of the software connection manager flows in tb.c. Also name the new function tb_increase_tmu_accuracy() to match what it actually does. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 43 ++++++++++++++++++++++++++++++++++--------- drivers/thunderbolt/tb.h | 2 -- drivers/thunderbolt/tmu.c | 29 ----------------------------- 3 files changed, 34 insertions(+), 40 deletions(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 72041e29e544..39ec7094fe17 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -240,6 +240,38 @@ static void tb_discover_dp_resources(struct tb *tb) } } +static int tb_increase_switch_tmu_accuracy(struct device *dev, void *data) +{ + struct tb_switch *sw; + + sw = tb_to_switch(dev); + if (sw) { + tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, + tb_switch_is_clx_enabled(sw, TB_CL1)); + if (tb_switch_tmu_enable(sw)) + tb_sw_warn(sw, "failed to increase TMU rate\n"); + } + + return 0; +} + +static void tb_increase_tmu_accuracy(struct tb_tunnel *tunnel) +{ + struct tb_switch *sw; + + if (!tunnel) + return; + + /* + * Once first DP tunnel is established we change the TMU + * accuracy of first depth child routers (and the host router) + * to the highest. This is needed for the DP tunneling to work + * but also allows CL0s. + */ + sw = tunnel->tb->root_switch; + device_for_each_child(&sw->dev, NULL, tb_increase_switch_tmu_accuracy); +} + static void tb_switch_discover_tunnels(struct tb_switch *sw, struct list_head *list, bool alloc_hopids) @@ -253,13 +285,7 @@ static void tb_switch_discover_tunnels(struct tb_switch *sw, switch (port->config.type) { case TB_TYPE_DP_HDMI_IN: tunnel = tb_tunnel_discover_dp(tb, port, alloc_hopids); - /* - * In case of DP tunnel exists, change host router's - * 1st children TMU mode to HiFi for CL0s to work. - */ - if (tunnel) - tb_switch_enable_tmu_1st_child(tb->root_switch, - TB_SWITCH_TMU_RATE_HIFI); + tb_increase_tmu_accuracy(tunnel); break; case TB_TYPE_PCIE_DOWN: @@ -1263,8 +1289,7 @@ static void tb_tunnel_dp(struct tb *tb) * In case of DP tunnel exists, change host router's 1st children * TMU mode to HiFi for CL0s to work. */ - tb_switch_enable_tmu_1st_child(tb->root_switch, TB_SWITCH_TMU_RATE_HIFI); - + tb_increase_tmu_accuracy(tunnel); return; err_free: diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 75dbe00d7cf6..5c4b671092bf 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -990,8 +990,6 @@ int tb_switch_tmu_enable(struct tb_switch *sw); void tb_switch_tmu_configure(struct tb_switch *sw, enum tb_switch_tmu_rate rate, bool unidirectional); -void tb_switch_enable_tmu_1st_child(struct tb_switch *sw, - enum tb_switch_tmu_rate rate); /** * tb_switch_tmu_is_enabled() - Checks if the specified TMU mode is enabled * @sw: Router whose TMU mode to check diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index 30f18806abb7..84abb783a6d9 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -731,32 +731,3 @@ void tb_switch_tmu_configure(struct tb_switch *sw, sw->tmu.unidirectional_request = unidirectional; sw->tmu.rate_request = rate; } - -static int tb_switch_tmu_config_enable(struct device *dev, void *rate) -{ - if (tb_is_switch(dev)) { - struct tb_switch *sw = tb_to_switch(dev); - - tb_switch_tmu_configure(sw, *(enum tb_switch_tmu_rate *)rate, - tb_switch_is_clx_enabled(sw, TB_CL1)); - if (tb_switch_tmu_enable(sw)) - tb_sw_dbg(sw, "fail switching TMU mode for 1st depth router\n"); - } - - return 0; -} - -/** - * tb_switch_enable_tmu_1st_child - Configure and enable TMU for 1st chidren - * @sw: The router to configure and enable it's children TMU - * @rate: Rate of the TMU to configure the router's chidren to - * - * Configures and enables the TMU mode of 1st depth children of the specified - * router to the specified rate. - */ -void tb_switch_enable_tmu_1st_child(struct tb_switch *sw, - enum tb_switch_tmu_rate rate) -{ - device_for_each_child(&sw->dev, &rate, - tb_switch_tmu_config_enable); -} -- cgit v1.2.3 From 20c2fae9dbe35134e98679181faa611cc69c6c30 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 7 Oct 2022 16:53:58 +0300 Subject: thunderbolt: Move TMU configuration to tb_enable_tmu() There is no need to duplicate the code the enables TMU. Also update the comment to better explain why we do this in the first place. No functional changes. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 39ec7094fe17..0630b877136e 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -387,6 +387,16 @@ static int tb_enable_tmu(struct tb_switch *sw) { int ret; + /* + * If CL1 is enabled then we need to configure the TMU accuracy + * level to normal. Otherwise we keep the TMU running at the + * highest accuracy. + */ + if (tb_switch_is_clx_enabled(sw, TB_CL1)) + tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_NORMAL, true); + else + tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, false); + /* If it is already enabled in correct mode, don't touch it */ if (tb_switch_tmu_is_enabled(sw)) return 0; @@ -873,16 +883,6 @@ static void tb_scan_port(struct tb_port *port) tb_switch_clx_name(TB_CL1)); } - if (tb_switch_is_clx_enabled(sw, TB_CL1)) - /* - * To support highest CLx state, we set router's TMU to - * Normal-Uni mode. - */ - tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_NORMAL, true); - else - /* If CLx disabled, configure router's TMU to HiFi-Bidir mode*/ - tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, false); - if (tb_enable_tmu(sw)) tb_sw_warn(sw, "failed to enable TMU\n"); @@ -2035,16 +2035,6 @@ static void tb_restore_children(struct tb_switch *sw) tb_sw_warn(sw, "failed to re-enable %s on upstream port\n", tb_switch_clx_name(TB_CL1)); - if (tb_switch_is_clx_enabled(sw, TB_CL1)) - /* - * To support highest CLx state, we set router's TMU to - * Normal-Uni mode. - */ - tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_NORMAL, true); - else - /* If CLx disabled, configure router's TMU to HiFi-Bidir mode*/ - tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, false); - if (tb_enable_tmu(sw)) tb_sw_warn(sw, "failed to restore TMU configuration\n"); -- cgit v1.2.3 From 4e7b4955cba13bb8b8f63400fbd28081f11179a7 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 7 Oct 2022 17:49:01 +0300 Subject: thunderbolt: Move tb_enable_tmu() close to other TMU functions This makes the code easier to follow. No functional changes. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 58 ++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 0630b877136e..41c353f462e7 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -272,6 +272,35 @@ static void tb_increase_tmu_accuracy(struct tb_tunnel *tunnel) device_for_each_child(&sw->dev, NULL, tb_increase_switch_tmu_accuracy); } +static int tb_enable_tmu(struct tb_switch *sw) +{ + int ret; + + /* + * If CL1 is enabled then we need to configure the TMU accuracy + * level to normal. Otherwise we keep the TMU running at the + * highest accuracy. + */ + if (tb_switch_is_clx_enabled(sw, TB_CL1)) + tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_NORMAL, true); + else + tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, false); + + /* If it is already enabled in correct mode, don't touch it */ + if (tb_switch_tmu_is_enabled(sw)) + return 0; + + ret = tb_switch_tmu_disable(sw); + if (ret) + return ret; + + ret = tb_switch_tmu_post_time(sw); + if (ret) + return ret; + + return tb_switch_tmu_enable(sw); +} + static void tb_switch_discover_tunnels(struct tb_switch *sw, struct list_head *list, bool alloc_hopids) @@ -383,35 +412,6 @@ static void tb_scan_xdomain(struct tb_port *port) } } -static int tb_enable_tmu(struct tb_switch *sw) -{ - int ret; - - /* - * If CL1 is enabled then we need to configure the TMU accuracy - * level to normal. Otherwise we keep the TMU running at the - * highest accuracy. - */ - if (tb_switch_is_clx_enabled(sw, TB_CL1)) - tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_NORMAL, true); - else - tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, false); - - /* If it is already enabled in correct mode, don't touch it */ - if (tb_switch_tmu_is_enabled(sw)) - return 0; - - ret = tb_switch_tmu_disable(sw); - if (ret) - return ret; - - ret = tb_switch_tmu_post_time(sw); - if (ret) - return ret; - - return tb_switch_tmu_enable(sw); -} - /** * tb_find_unused_port() - return the first inactive port on @sw * @sw: Switch to find the port on -- cgit v1.2.3 From ef34add89ee4d1473bd0e78f551efa6fc1feb0cd Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 10 Oct 2022 18:04:16 +0300 Subject: thunderbolt: Check valid TMU configuration in tb_switch_tmu_configure() Instead of at enable time we can do this already in tb_switch_tmu_configure(). Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 6 ++++-- drivers/thunderbolt/tb.h | 5 ++--- drivers/thunderbolt/tmu.c | 13 ++++++++----- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 41c353f462e7..91459bf2fd0f 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -282,9 +282,11 @@ static int tb_enable_tmu(struct tb_switch *sw) * highest accuracy. */ if (tb_switch_is_clx_enabled(sw, TB_CL1)) - tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_NORMAL, true); + ret = tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_NORMAL, true); else - tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, false); + ret = tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, false); + if (ret) + return ret; /* If it is already enabled in correct mode, don't touch it */ if (tb_switch_tmu_is_enabled(sw)) diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 5c4b671092bf..2ecad0a6c6a4 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -987,9 +987,8 @@ int tb_switch_tmu_init(struct tb_switch *sw); int tb_switch_tmu_post_time(struct tb_switch *sw); int tb_switch_tmu_disable(struct tb_switch *sw); int tb_switch_tmu_enable(struct tb_switch *sw); -void tb_switch_tmu_configure(struct tb_switch *sw, - enum tb_switch_tmu_rate rate, - bool unidirectional); +int tb_switch_tmu_configure(struct tb_switch *sw, enum tb_switch_tmu_rate rate, + bool unidirectional); /** * tb_switch_tmu_is_enabled() - Checks if the specified TMU mode is enabled * @sw: Router whose TMU mode to check diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index 84abb783a6d9..be310d97ea7b 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -648,9 +648,6 @@ int tb_switch_tmu_enable(struct tb_switch *sw) bool unidirectional = sw->tmu.unidirectional_request; int ret; - if (unidirectional && !sw->tmu.has_ucap) - return -EOPNOTSUPP; - /* * No need to enable TMU on devices that don't support CLx since on * these devices e.g. Alpine Ridge and earlier, the TMU mode HiFi @@ -724,10 +721,16 @@ int tb_switch_tmu_enable(struct tb_switch *sw) * * Selects the rate of the TMU and directionality (uni-directional or * bi-directional). Must be called before tb_switch_tmu_enable(). + * + * Returns %0 in success and negative errno otherwise. */ -void tb_switch_tmu_configure(struct tb_switch *sw, - enum tb_switch_tmu_rate rate, bool unidirectional) +int tb_switch_tmu_configure(struct tb_switch *sw, enum tb_switch_tmu_rate rate, + bool unidirectional) { + if (unidirectional && !sw->tmu.has_ucap) + return -EINVAL; + sw->tmu.unidirectional_request = unidirectional; sw->tmu.rate_request = rate; + return 0; } -- cgit v1.2.3 From 12a14f2fca32332d065b64f6f654fa332c90475e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 7 Oct 2022 18:12:02 +0300 Subject: thunderbolt: Move CLx support functions into clx.c There really don't belong to switch.c so move them into their own file. As we do this rename the functions to match the conventions used elsewhere in the driver. No functional changes. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/Makefile | 2 +- drivers/thunderbolt/clx.c | 362 ++++++++++++++++++++++++++++++++++++++++++ drivers/thunderbolt/debugfs.c | 2 +- drivers/thunderbolt/switch.c | 362 +----------------------------------------- drivers/thunderbolt/tb.c | 8 +- drivers/thunderbolt/tb.h | 17 +- drivers/thunderbolt/tmu.c | 6 +- 7 files changed, 381 insertions(+), 378 deletions(-) create mode 100644 drivers/thunderbolt/clx.c diff --git a/drivers/thunderbolt/Makefile b/drivers/thunderbolt/Makefile index 78fd365893c1..c8b3d7b78098 100644 --- a/drivers/thunderbolt/Makefile +++ b/drivers/thunderbolt/Makefile @@ -2,7 +2,7 @@ obj-${CONFIG_USB4} := thunderbolt.o thunderbolt-objs := nhi.o nhi_ops.o ctl.o tb.o switch.o cap.o path.o tunnel.o eeprom.o thunderbolt-objs += domain.o dma_port.o icm.o property.o xdomain.o lc.o tmu.o usb4.o -thunderbolt-objs += usb4_port.o nvm.o retimer.o quirks.o +thunderbolt-objs += usb4_port.o nvm.o retimer.o quirks.o clx.o thunderbolt-${CONFIG_ACPI} += acpi.o thunderbolt-$(CONFIG_DEBUG_FS) += debugfs.o diff --git a/drivers/thunderbolt/clx.c b/drivers/thunderbolt/clx.c new file mode 100644 index 000000000000..d5b46a8e57c9 --- /dev/null +++ b/drivers/thunderbolt/clx.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CLx support + * + * Copyright (C) 2020 - 2023, Intel Corporation + * Authors: Gil Fine + * Mika Westerberg + */ + +#include + +#include "tb.h" + +static bool clx_enabled = true; +module_param_named(clx, clx_enabled, bool, 0444); +MODULE_PARM_DESC(clx, "allow low power states on the high-speed lanes (default: true)"); + +static int tb_port_pm_secondary_set(struct tb_port *port, bool secondary) +{ + u32 phy; + int ret; + + ret = tb_port_read(port, &phy, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_1, 1); + if (ret) + return ret; + + if (secondary) + phy |= LANE_ADP_CS_1_PMS; + else + phy &= ~LANE_ADP_CS_1_PMS; + + return tb_port_write(port, &phy, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_1, 1); +} + +static int tb_port_pm_secondary_enable(struct tb_port *port) +{ + return tb_port_pm_secondary_set(port, true); +} + +static int tb_port_pm_secondary_disable(struct tb_port *port) +{ + return tb_port_pm_secondary_set(port, false); +} + +/* Called for USB4 or Titan Ridge routers only */ +static bool tb_port_clx_supported(struct tb_port *port, unsigned int clx_mask) +{ + u32 val, mask = 0; + bool ret; + + /* Don't enable CLx in case of two single-lane links */ + if (!port->bonded && port->dual_link_port) + return false; + + /* Don't enable CLx in case of inter-domain link */ + if (port->xdomain) + return false; + + if (tb_switch_is_usb4(port->sw)) { + if (!usb4_port_clx_supported(port)) + return false; + } else if (!tb_lc_is_clx_supported(port)) { + return false; + } + + if (clx_mask & TB_CL1) { + /* CL0s and CL1 are enabled and supported together */ + mask |= LANE_ADP_CS_0_CL0S_SUPPORT | LANE_ADP_CS_0_CL1_SUPPORT; + } + if (clx_mask & TB_CL2) + mask |= LANE_ADP_CS_0_CL2_SUPPORT; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_0, 1); + if (ret) + return false; + + return !!(val & mask); +} + +static int tb_port_clx_set(struct tb_port *port, enum tb_clx clx, bool enable) +{ + u32 phy, mask; + int ret; + + /* CL0s and CL1 are enabled and supported together */ + if (clx == TB_CL1) + mask = LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE; + else + /* For now we support only CL0s and CL1. Not CL2 */ + return -EOPNOTSUPP; + + ret = tb_port_read(port, &phy, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_1, 1); + if (ret) + return ret; + + if (enable) + phy |= mask; + else + phy &= ~mask; + + return tb_port_write(port, &phy, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_1, 1); +} + +static int tb_port_clx_disable(struct tb_port *port, enum tb_clx clx) +{ + return tb_port_clx_set(port, clx, false); +} + +static int tb_port_clx_enable(struct tb_port *port, enum tb_clx clx) +{ + return tb_port_clx_set(port, clx, true); +} + +/** + * tb_port_clx_is_enabled() - Is given CL state enabled + * @port: USB4 port to check + * @clx_mask: Mask of CL states to check + * + * Returns true if any of the given CL states is enabled for @port. + */ +bool tb_port_clx_is_enabled(struct tb_port *port, unsigned int clx_mask) +{ + u32 val, mask = 0; + int ret; + + if (!tb_port_clx_supported(port, clx_mask)) + return false; + + if (clx_mask & TB_CL1) + mask |= LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE; + if (clx_mask & TB_CL2) + mask |= LANE_ADP_CS_1_CL2_ENABLE; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_1, 1); + if (ret) + return false; + + return !!(val & mask); +} + +static int tb_switch_pm_secondary_resolve(struct tb_switch *sw) +{ + struct tb_port *up, *down; + int ret; + + if (!tb_route(sw)) + return 0; + + up = tb_upstream_port(sw); + down = tb_switch_downstream_port(sw); + ret = tb_port_pm_secondary_enable(up); + if (ret) + return ret; + + return tb_port_pm_secondary_disable(down); +} + +static int tb_switch_mask_clx_objections(struct tb_switch *sw) +{ + int up_port = sw->config.upstream_port_number; + u32 offset, val[2], mask_obj, unmask_obj; + int ret, i; + + /* Only Titan Ridge of pre-USB4 devices support CLx states */ + if (!tb_switch_is_titan_ridge(sw)) + return 0; + + if (!tb_route(sw)) + return 0; + + /* + * In Titan Ridge there are only 2 dual-lane Thunderbolt ports: + * Port A consists of lane adapters 1,2 and + * Port B consists of lane adapters 3,4 + * If upstream port is A, (lanes are 1,2), we mask objections from + * port B (lanes 3,4) and unmask objections from Port A and vice-versa. + */ + if (up_port == 1) { + mask_obj = TB_LOW_PWR_C0_PORT_B_MASK; + unmask_obj = TB_LOW_PWR_C1_PORT_A_MASK; + offset = TB_LOW_PWR_C1_CL1; + } else { + mask_obj = TB_LOW_PWR_C1_PORT_A_MASK; + unmask_obj = TB_LOW_PWR_C0_PORT_B_MASK; + offset = TB_LOW_PWR_C3_CL1; + } + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, + sw->cap_lp + offset, ARRAY_SIZE(val)); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(val); i++) { + val[i] |= mask_obj; + val[i] &= ~unmask_obj; + } + + return tb_sw_write(sw, &val, TB_CFG_SWITCH, + sw->cap_lp + offset, ARRAY_SIZE(val)); +} + +static int __tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx) +{ + bool up_clx_support, down_clx_support; + struct tb_port *up, *down; + int ret; + + if (!tb_switch_clx_is_supported(sw)) + return 0; + + /* + * Enable CLx for host router's downstream port as part of the + * downstream router enabling procedure. + */ + if (!tb_route(sw)) + return 0; + + /* Enable CLx only for first hop router (depth = 1) */ + if (tb_route(tb_switch_parent(sw))) + return 0; + + ret = tb_switch_pm_secondary_resolve(sw); + if (ret) + return ret; + + up = tb_upstream_port(sw); + down = tb_switch_downstream_port(sw); + + up_clx_support = tb_port_clx_supported(up, clx); + down_clx_support = tb_port_clx_supported(down, clx); + + tb_port_dbg(up, "%s %ssupported\n", tb_switch_clx_name(clx), + up_clx_support ? "" : "not "); + tb_port_dbg(down, "%s %ssupported\n", tb_switch_clx_name(clx), + down_clx_support ? "" : "not "); + + if (!up_clx_support || !down_clx_support) + return -EOPNOTSUPP; + + ret = tb_port_clx_enable(up, clx); + if (ret) + return ret; + + ret = tb_port_clx_enable(down, clx); + if (ret) { + tb_port_clx_disable(up, clx); + return ret; + } + + ret = tb_switch_mask_clx_objections(sw); + if (ret) { + tb_port_clx_disable(up, clx); + tb_port_clx_disable(down, clx); + return ret; + } + + sw->clx = clx; + + tb_port_dbg(up, "%s enabled\n", tb_switch_clx_name(clx)); + return 0; +} + +/** + * tb_switch_clx_enable() - Enable CLx on upstream port of specified router + * @sw: Router to enable CLx for + * @clx: The CLx state to enable + * + * Enable CLx state only for first hop router. That is the most common + * use-case, that is intended for better thermal management, and so helps + * to improve performance. CLx is enabled only if both sides of the link + * support CLx, and if both sides of the link are not configured as two + * single lane links and only if the link is not inter-domain link. The + * complete set of conditions is described in CM Guide 1.0 section 8.1. + * + * Return: Returns 0 on success or an error code on failure. + */ +int tb_switch_clx_enable(struct tb_switch *sw, enum tb_clx clx) +{ + struct tb_switch *root_sw = sw->tb->root_switch; + + if (!clx_enabled) + return 0; + + /* + * CLx is not enabled and validated on Intel USB4 platforms before + * Alder Lake. + */ + if (root_sw->generation < 4 || tb_switch_is_tiger_lake(root_sw)) + return 0; + + switch (clx) { + case TB_CL1: + /* CL0s and CL1 are enabled and supported together */ + return __tb_switch_enable_clx(sw, clx); + + default: + return -EOPNOTSUPP; + } +} + +static int __tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx) +{ + struct tb_port *up, *down; + int ret; + + if (!tb_switch_clx_is_supported(sw)) + return 0; + + /* + * Disable CLx for host router's downstream port as part of the + * downstream router enabling procedure. + */ + if (!tb_route(sw)) + return 0; + + /* Disable CLx only for first hop router (depth = 1) */ + if (tb_route(tb_switch_parent(sw))) + return 0; + + up = tb_upstream_port(sw); + down = tb_switch_downstream_port(sw); + ret = tb_port_clx_disable(up, clx); + if (ret) + return ret; + + ret = tb_port_clx_disable(down, clx); + if (ret) + return ret; + + sw->clx = TB_CLX_DISABLE; + + tb_port_dbg(up, "%s disabled\n", tb_switch_clx_name(clx)); + return 0; +} + +/** + * tb_switch_cls_disable() - Disable CLx on upstream port of specified router + * @sw: Router to disable CLx for + * @clx: The CLx state to disable + * + * Return: Returns 0 on success or an error code on failure. + */ +int tb_switch_clx_disable(struct tb_switch *sw, enum tb_clx clx) +{ + if (!clx_enabled) + return 0; + + switch (clx) { + case TB_CL1: + /* CL0s and CL1 are enabled and supported together */ + return __tb_switch_disable_clx(sw, clx); + + default: + return -EOPNOTSUPP; + } +} diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index f92ad71ef983..e376ad25bf60 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -570,7 +570,7 @@ static int margining_run_write(void *data, u64 val) * CL states may interfere with lane margining so inform the user know * and bail out. */ - if (tb_port_is_clx_enabled(port, TB_CL1 | TB_CL2)) { + if (tb_port_clx_is_enabled(port, TB_CL1 | TB_CL2)) { tb_port_warn(port, "CL states are enabled, Disable them with clx=0 and re-connect\n"); ret = -EINVAL; diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 4f3d02c58c9e..984b5536e143 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -26,10 +26,6 @@ struct nvm_auth_status { u32 status; }; -static bool clx_enabled = true; -module_param_named(clx, clx_enabled, bool, 0444); -MODULE_PARM_DESC(clx, "allow low power states on the high-speed lanes (default: true)"); - /* * Hold NVM authentication failure status per switch This information * needs to stay around even when the switch gets power cycled so we @@ -1183,135 +1179,6 @@ int tb_port_update_credits(struct tb_port *port) return tb_port_do_update_credits(port->dual_link_port); } -static int __tb_port_pm_secondary_set(struct tb_port *port, bool secondary) -{ - u32 phy; - int ret; - - ret = tb_port_read(port, &phy, TB_CFG_PORT, - port->cap_phy + LANE_ADP_CS_1, 1); - if (ret) - return ret; - - if (secondary) - phy |= LANE_ADP_CS_1_PMS; - else - phy &= ~LANE_ADP_CS_1_PMS; - - return tb_port_write(port, &phy, TB_CFG_PORT, - port->cap_phy + LANE_ADP_CS_1, 1); -} - -static int tb_port_pm_secondary_enable(struct tb_port *port) -{ - return __tb_port_pm_secondary_set(port, true); -} - -static int tb_port_pm_secondary_disable(struct tb_port *port) -{ - return __tb_port_pm_secondary_set(port, false); -} - -/* Called for USB4 or Titan Ridge routers only */ -static bool tb_port_clx_supported(struct tb_port *port, unsigned int clx_mask) -{ - u32 val, mask = 0; - bool ret; - - /* Don't enable CLx in case of two single-lane links */ - if (!port->bonded && port->dual_link_port) - return false; - - /* Don't enable CLx in case of inter-domain link */ - if (port->xdomain) - return false; - - if (tb_switch_is_usb4(port->sw)) { - if (!usb4_port_clx_supported(port)) - return false; - } else if (!tb_lc_is_clx_supported(port)) { - return false; - } - - if (clx_mask & TB_CL1) { - /* CL0s and CL1 are enabled and supported together */ - mask |= LANE_ADP_CS_0_CL0S_SUPPORT | LANE_ADP_CS_0_CL1_SUPPORT; - } - if (clx_mask & TB_CL2) - mask |= LANE_ADP_CS_0_CL2_SUPPORT; - - ret = tb_port_read(port, &val, TB_CFG_PORT, - port->cap_phy + LANE_ADP_CS_0, 1); - if (ret) - return false; - - return !!(val & mask); -} - -static int __tb_port_clx_set(struct tb_port *port, enum tb_clx clx, bool enable) -{ - u32 phy, mask; - int ret; - - /* CL0s and CL1 are enabled and supported together */ - if (clx == TB_CL1) - mask = LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE; - else - /* For now we support only CL0s and CL1. Not CL2 */ - return -EOPNOTSUPP; - - ret = tb_port_read(port, &phy, TB_CFG_PORT, - port->cap_phy + LANE_ADP_CS_1, 1); - if (ret) - return ret; - - if (enable) - phy |= mask; - else - phy &= ~mask; - - return tb_port_write(port, &phy, TB_CFG_PORT, - port->cap_phy + LANE_ADP_CS_1, 1); -} - -static int tb_port_clx_disable(struct tb_port *port, enum tb_clx clx) -{ - return __tb_port_clx_set(port, clx, false); -} - -static int tb_port_clx_enable(struct tb_port *port, enum tb_clx clx) -{ - return __tb_port_clx_set(port, clx, true); -} - -/** - * tb_port_is_clx_enabled() - Is given CL state enabled - * @port: USB4 port to check - * @clx_mask: Mask of CL states to check - * - * Returns true if any of the given CL states is enabled for @port. - */ -bool tb_port_is_clx_enabled(struct tb_port *port, unsigned int clx_mask) -{ - u32 val, mask = 0; - int ret; - - if (!tb_port_clx_supported(port, clx_mask)) - return false; - - if (clx_mask & TB_CL1) - mask |= LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE; - if (clx_mask & TB_CL2) - mask |= LANE_ADP_CS_1_CL2_ENABLE; - - ret = tb_port_read(port, &val, TB_CFG_PORT, - port->cap_phy + LANE_ADP_CS_1, 1); - if (ret) - return false; - - return !!(val & mask); -} - static int tb_port_start_lane_initialization(struct tb_port *port) { int ret; @@ -3246,8 +3113,8 @@ void tb_switch_suspend(struct tb_switch *sw, bool runtime) * done for USB4 device too as CLx is re-enabled at resume. * CL0s and CL1 are enabled and supported together. */ - if (tb_switch_is_clx_enabled(sw, TB_CL1)) { - if (tb_switch_disable_clx(sw, TB_CL1)) + if (tb_switch_clx_is_enabled(sw, TB_CL1)) { + if (tb_switch_clx_disable(sw, TB_CL1)) tb_sw_warn(sw, "failed to disable %s on upstream port\n", tb_switch_clx_name(TB_CL1)); } @@ -3472,231 +3339,6 @@ struct tb_port *tb_switch_find_port(struct tb_switch *sw, return NULL; } -static int tb_switch_pm_secondary_resolve(struct tb_switch *sw) -{ - struct tb_port *up, *down; - int ret; - - if (!tb_route(sw)) - return 0; - - up = tb_upstream_port(sw); - down = tb_switch_downstream_port(sw); - ret = tb_port_pm_secondary_enable(up); - if (ret) - return ret; - - return tb_port_pm_secondary_disable(down); -} - -static int __tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx) -{ - bool up_clx_support, down_clx_support; - struct tb_port *up, *down; - int ret; - - if (!tb_switch_is_clx_supported(sw)) - return 0; - - /* - * Enable CLx for host router's downstream port as part of the - * downstream router enabling procedure. - */ - if (!tb_route(sw)) - return 0; - - /* Enable CLx only for first hop router (depth = 1) */ - if (tb_route(tb_switch_parent(sw))) - return 0; - - ret = tb_switch_pm_secondary_resolve(sw); - if (ret) - return ret; - - up = tb_upstream_port(sw); - down = tb_switch_downstream_port(sw); - - up_clx_support = tb_port_clx_supported(up, clx); - down_clx_support = tb_port_clx_supported(down, clx); - - tb_port_dbg(up, "%s %ssupported\n", tb_switch_clx_name(clx), - up_clx_support ? "" : "not "); - tb_port_dbg(down, "%s %ssupported\n", tb_switch_clx_name(clx), - down_clx_support ? "" : "not "); - - if (!up_clx_support || !down_clx_support) - return -EOPNOTSUPP; - - ret = tb_port_clx_enable(up, clx); - if (ret) - return ret; - - ret = tb_port_clx_enable(down, clx); - if (ret) { - tb_port_clx_disable(up, clx); - return ret; - } - - ret = tb_switch_mask_clx_objections(sw); - if (ret) { - tb_port_clx_disable(up, clx); - tb_port_clx_disable(down, clx); - return ret; - } - - sw->clx = clx; - - tb_port_dbg(up, "%s enabled\n", tb_switch_clx_name(clx)); - return 0; -} - -/** - * tb_switch_enable_clx() - Enable CLx on upstream port of specified router - * @sw: Router to enable CLx for - * @clx: The CLx state to enable - * - * Enable CLx state only for first hop router. That is the most common - * use-case, that is intended for better thermal management, and so helps - * to improve performance. CLx is enabled only if both sides of the link - * support CLx, and if both sides of the link are not configured as two - * single lane links and only if the link is not inter-domain link. The - * complete set of conditions is described in CM Guide 1.0 section 8.1. - * - * Return: Returns 0 on success or an error code on failure. - */ -int tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx) -{ - struct tb_switch *root_sw = sw->tb->root_switch; - - if (!clx_enabled) - return 0; - - /* - * CLx is not enabled and validated on Intel USB4 platforms before - * Alder Lake. - */ - if (root_sw->generation < 4 || tb_switch_is_tiger_lake(root_sw)) - return 0; - - switch (clx) { - case TB_CL1: - /* CL0s and CL1 are enabled and supported together */ - return __tb_switch_enable_clx(sw, clx); - - default: - return -EOPNOTSUPP; - } -} - -static int __tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx) -{ - struct tb_port *up, *down; - int ret; - - if (!tb_switch_is_clx_supported(sw)) - return 0; - - /* - * Disable CLx for host router's downstream port as part of the - * downstream router enabling procedure. - */ - if (!tb_route(sw)) - return 0; - - /* Disable CLx only for first hop router (depth = 1) */ - if (tb_route(tb_switch_parent(sw))) - return 0; - - up = tb_upstream_port(sw); - down = tb_switch_downstream_port(sw); - ret = tb_port_clx_disable(up, clx); - if (ret) - return ret; - - ret = tb_port_clx_disable(down, clx); - if (ret) - return ret; - - sw->clx = TB_CLX_DISABLE; - - tb_port_dbg(up, "%s disabled\n", tb_switch_clx_name(clx)); - return 0; -} - -/** - * tb_switch_disable_clx() - Disable CLx on upstream port of specified router - * @sw: Router to disable CLx for - * @clx: The CLx state to disable - * - * Return: Returns 0 on success or an error code on failure. - */ -int tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx) -{ - if (!clx_enabled) - return 0; - - switch (clx) { - case TB_CL1: - /* CL0s and CL1 are enabled and supported together */ - return __tb_switch_disable_clx(sw, clx); - - default: - return -EOPNOTSUPP; - } -} - -/** - * tb_switch_mask_clx_objections() - Mask CLx objections for a router - * @sw: Router to mask objections for - * - * Mask the objections coming from the second depth routers in order to - * stop these objections from interfering with the CLx states of the first - * depth link. - */ -int tb_switch_mask_clx_objections(struct tb_switch *sw) -{ - int up_port = sw->config.upstream_port_number; - u32 offset, val[2], mask_obj, unmask_obj; - int ret, i; - - /* Only Titan Ridge of pre-USB4 devices support CLx states */ - if (!tb_switch_is_titan_ridge(sw)) - return 0; - - if (!tb_route(sw)) - return 0; - - /* - * In Titan Ridge there are only 2 dual-lane Thunderbolt ports: - * Port A consists of lane adapters 1,2 and - * Port B consists of lane adapters 3,4 - * If upstream port is A, (lanes are 1,2), we mask objections from - * port B (lanes 3,4) and unmask objections from Port A and vice-versa. - */ - if (up_port == 1) { - mask_obj = TB_LOW_PWR_C0_PORT_B_MASK; - unmask_obj = TB_LOW_PWR_C1_PORT_A_MASK; - offset = TB_LOW_PWR_C1_CL1; - } else { - mask_obj = TB_LOW_PWR_C1_PORT_A_MASK; - unmask_obj = TB_LOW_PWR_C0_PORT_B_MASK; - offset = TB_LOW_PWR_C3_CL1; - } - - ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, - sw->cap_lp + offset, ARRAY_SIZE(val)); - if (ret) - return ret; - - for (i = 0; i < ARRAY_SIZE(val); i++) { - val[i] |= mask_obj; - val[i] &= ~unmask_obj; - } - - return tb_sw_write(sw, &val, TB_CFG_SWITCH, - sw->cap_lp + offset, ARRAY_SIZE(val)); -} - /* * Can be used for read/write a specified PCIe bridge for any Thunderbolt 3 * device. For now used only for Titan Ridge. diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 91459bf2fd0f..c7cfd740520a 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -247,7 +247,7 @@ static int tb_increase_switch_tmu_accuracy(struct device *dev, void *data) sw = tb_to_switch(dev); if (sw) { tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, - tb_switch_is_clx_enabled(sw, TB_CL1)); + tb_switch_clx_is_enabled(sw, TB_CL1)); if (tb_switch_tmu_enable(sw)) tb_sw_warn(sw, "failed to increase TMU rate\n"); } @@ -281,7 +281,7 @@ static int tb_enable_tmu(struct tb_switch *sw) * level to normal. Otherwise we keep the TMU running at the * highest accuracy. */ - if (tb_switch_is_clx_enabled(sw, TB_CL1)) + if (tb_switch_clx_is_enabled(sw, TB_CL1)) ret = tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_NORMAL, true); else ret = tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, false); @@ -879,7 +879,7 @@ static void tb_scan_port(struct tb_port *port) if (discovery) { tb_sw_dbg(sw, "discovery, not touching CL states\n"); } else { - ret = tb_switch_enable_clx(sw, TB_CL1); + ret = tb_switch_clx_enable(sw, TB_CL1); if (ret && ret != -EOPNOTSUPP) tb_sw_warn(sw, "failed to enable %s on upstream port\n", tb_switch_clx_name(TB_CL1)); @@ -2032,7 +2032,7 @@ static void tb_restore_children(struct tb_switch *sw) * CL0s and CL1 are enabled and supported together. * Silently ignore CLx re-enabling in case CLx is not supported. */ - ret = tb_switch_enable_clx(sw, TB_CL1); + ret = tb_switch_clx_enable(sw, TB_CL1); if (ret && ret != -EOPNOTSUPP) tb_sw_warn(sw, "failed to re-enable %s on upstream port\n", tb_switch_clx_name(TB_CL1)); diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 2ecad0a6c6a4..23fbcc850c03 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1002,6 +1002,8 @@ static inline bool tb_switch_tmu_is_enabled(const struct tb_switch *sw) sw->tmu.unidirectional == sw->tmu.unidirectional_request; } +bool tb_port_clx_is_enabled(struct tb_port *port, unsigned int clx_mask); + static inline const char *tb_switch_clx_name(enum tb_clx clx) { switch (clx) { @@ -1013,28 +1015,28 @@ static inline const char *tb_switch_clx_name(enum tb_clx clx) } } -int tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx); -int tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx); +int tb_switch_clx_enable(struct tb_switch *sw, enum tb_clx clx); +int tb_switch_clx_disable(struct tb_switch *sw, enum tb_clx clx); /** - * tb_switch_is_clx_enabled() - Checks if the CLx is enabled + * tb_switch_clx_is_enabled() - Checks if the CLx is enabled * @sw: Router to check for the CLx * @clx: The CLx state to check for * * Checks if the specified CLx is enabled on the router upstream link. * Not applicable for a host router. */ -static inline bool tb_switch_is_clx_enabled(const struct tb_switch *sw, +static inline bool tb_switch_clx_is_enabled(const struct tb_switch *sw, enum tb_clx clx) { return sw->clx == clx; } /** - * tb_switch_is_clx_supported() - Is CLx supported on this type of router + * tb_switch_clx_is_supported() - Is CLx supported on this type of router * @sw: The router to check CLx support for */ -static inline bool tb_switch_is_clx_supported(const struct tb_switch *sw) +static inline bool tb_switch_clx_is_supported(const struct tb_switch *sw) { if (sw->quirks & QUIRK_NO_CLX) return false; @@ -1042,8 +1044,6 @@ static inline bool tb_switch_is_clx_supported(const struct tb_switch *sw) return tb_switch_is_usb4(sw) || tb_switch_is_titan_ridge(sw); } -int tb_switch_mask_clx_objections(struct tb_switch *sw); - int tb_switch_pcie_l1_enable(struct tb_switch *sw); int tb_switch_xhci_connect(struct tb_switch *sw); @@ -1089,7 +1089,6 @@ void tb_port_lane_bonding_disable(struct tb_port *port); int tb_port_wait_for_link_width(struct tb_port *port, int width, int timeout_msec); int tb_port_update_credits(struct tb_port *port); -bool tb_port_is_clx_enabled(struct tb_port *port, unsigned int clx); int tb_switch_find_vse_cap(struct tb_switch *sw, enum tb_switch_vse_cap vsec); int tb_switch_find_cap(struct tb_switch *sw, enum tb_switch_cap cap); diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index be310d97ea7b..6988704c845c 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -388,7 +388,7 @@ int tb_switch_tmu_disable(struct tb_switch *sw) * on these devices e.g. Alpine Ridge and earlier, the TMU mode * HiFi bi-directional is enabled by default and we don't change it. */ - if (!tb_switch_is_clx_supported(sw)) + if (!tb_switch_clx_is_supported(sw)) return 0; /* Already disabled? */ @@ -653,7 +653,7 @@ int tb_switch_tmu_enable(struct tb_switch *sw) * these devices e.g. Alpine Ridge and earlier, the TMU mode HiFi * bi-directional is enabled by default. */ - if (!tb_switch_is_clx_supported(sw)) + if (!tb_switch_clx_is_supported(sw)) return 0; if (tb_switch_tmu_is_enabled(sw)) @@ -664,7 +664,7 @@ int tb_switch_tmu_enable(struct tb_switch *sw) * Titan Ridge supports CL0s and CL1 only. CL0s and CL1 are * enabled and supported together. */ - if (!tb_switch_is_clx_enabled(sw, TB_CL1)) + if (!tb_switch_clx_is_enabled(sw, TB_CL1)) return -EOPNOTSUPP; ret = tb_switch_tmu_disable_objections(sw); -- cgit v1.2.3 From 4f9a4f25ade82463895a70096c722780deab000f Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 10 Oct 2022 12:10:33 +0300 Subject: thunderbolt: Get rid of __tb_switch_[en|dis]able_clx() No need to have separate functions for these so fold them into tb_switch_clx_enable() and tb_switch_clx_disable() accordingly. No functional changes. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/clx.c | 91 ++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 49 deletions(-) diff --git a/drivers/thunderbolt/clx.c b/drivers/thunderbolt/clx.c index d5b46a8e57c9..d1a502525425 100644 --- a/drivers/thunderbolt/clx.c +++ b/drivers/thunderbolt/clx.c @@ -205,12 +205,46 @@ static int tb_switch_mask_clx_objections(struct tb_switch *sw) sw->cap_lp + offset, ARRAY_SIZE(val)); } -static int __tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx) +/** + * tb_switch_clx_enable() - Enable CLx on upstream port of specified router + * @sw: Router to enable CLx for + * @clx: The CLx state to enable + * + * Enable CLx state only for first hop router. That is the most common + * use-case, that is intended for better thermal management, and so helps + * to improve performance. CLx is enabled only if both sides of the link + * support CLx, and if both sides of the link are not configured as two + * single lane links and only if the link is not inter-domain link. The + * complete set of conditions is described in CM Guide 1.0 section 8.1. + * + * Return: Returns 0 on success or an error code on failure. + */ +int tb_switch_clx_enable(struct tb_switch *sw, enum tb_clx clx) { + struct tb_switch *root_sw = sw->tb->root_switch; bool up_clx_support, down_clx_support; struct tb_port *up, *down; int ret; + if (!clx_enabled) + return 0; + + /* + * CLx is not enabled and validated on Intel USB4 platforms before + * Alder Lake. + */ + if (root_sw->generation < 4 || tb_switch_is_tiger_lake(root_sw)) + return 0; + + switch (clx) { + case TB_CL1: + /* CL0s and CL1 are enabled and supported together */ + break; + + default: + return -EOPNOTSUPP; + } + if (!tb_switch_clx_is_supported(sw)) return 0; @@ -267,47 +301,28 @@ static int __tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx) } /** - * tb_switch_clx_enable() - Enable CLx on upstream port of specified router - * @sw: Router to enable CLx for - * @clx: The CLx state to enable - * - * Enable CLx state only for first hop router. That is the most common - * use-case, that is intended for better thermal management, and so helps - * to improve performance. CLx is enabled only if both sides of the link - * support CLx, and if both sides of the link are not configured as two - * single lane links and only if the link is not inter-domain link. The - * complete set of conditions is described in CM Guide 1.0 section 8.1. + * tb_switch_clx_disable() - Disable CLx on upstream port of specified router + * @sw: Router to disable CLx for + * @clx: The CLx state to disable * * Return: Returns 0 on success or an error code on failure. */ -int tb_switch_clx_enable(struct tb_switch *sw, enum tb_clx clx) +int tb_switch_clx_disable(struct tb_switch *sw, enum tb_clx clx) { - struct tb_switch *root_sw = sw->tb->root_switch; + struct tb_port *up, *down; + int ret; if (!clx_enabled) return 0; - /* - * CLx is not enabled and validated on Intel USB4 platforms before - * Alder Lake. - */ - if (root_sw->generation < 4 || tb_switch_is_tiger_lake(root_sw)) - return 0; - switch (clx) { case TB_CL1: /* CL0s and CL1 are enabled and supported together */ - return __tb_switch_enable_clx(sw, clx); + break; default: return -EOPNOTSUPP; } -} - -static int __tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx) -{ - struct tb_port *up, *down; - int ret; if (!tb_switch_clx_is_supported(sw)) return 0; @@ -338,25 +353,3 @@ static int __tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx) tb_port_dbg(up, "%s disabled\n", tb_switch_clx_name(clx)); return 0; } - -/** - * tb_switch_cls_disable() - Disable CLx on upstream port of specified router - * @sw: Router to disable CLx for - * @clx: The CLx state to disable - * - * Return: Returns 0 on success or an error code on failure. - */ -int tb_switch_clx_disable(struct tb_switch *sw, enum tb_clx clx) -{ - if (!clx_enabled) - return 0; - - switch (clx) { - case TB_CL1: - /* CL0s and CL1 are enabled and supported together */ - return __tb_switch_disable_clx(sw, clx); - - default: - return -EOPNOTSUPP; - } -} -- cgit v1.2.3 From 1a9b6cb8b5dbd4a8425c7d774e911c0e22e4db56 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 10 Oct 2022 11:31:07 +0300 Subject: thunderbolt: Move CLx enabling into tb_enable_clx() This avoids some duplication and makes the flow slightly easier to understand. Also follows what we do in tb_enable_tmu(). Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index c7cfd740520a..e4f1233eb958 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -240,6 +240,18 @@ static void tb_discover_dp_resources(struct tb *tb) } } +static int tb_enable_clx(struct tb_switch *sw) +{ + int ret; + + /* + * CL0s and CL1 are enabled and supported together. + * Silently ignore CLx enabling in case CLx is not supported. + */ + ret = tb_switch_clx_enable(sw, TB_CL1); + return ret == -EOPNOTSUPP ? 0 : ret; +} + static int tb_increase_switch_tmu_accuracy(struct device *dev, void *data) { struct tb_switch *sw; @@ -777,7 +789,6 @@ static void tb_scan_port(struct tb_port *port) struct tb_port *upstream_port; bool discovery = false; struct tb_switch *sw; - int ret; if (tb_is_upstream_port(port)) return; @@ -876,14 +887,10 @@ static void tb_scan_port(struct tb_port *port) * CL0s and CL1 are enabled and supported together. * Silently ignore CLx enabling in case CLx is not supported. */ - if (discovery) { + if (discovery) tb_sw_dbg(sw, "discovery, not touching CL states\n"); - } else { - ret = tb_switch_clx_enable(sw, TB_CL1); - if (ret && ret != -EOPNOTSUPP) - tb_sw_warn(sw, "failed to enable %s on upstream port\n", - tb_switch_clx_name(TB_CL1)); - } + else if (tb_enable_clx(sw)) + tb_sw_warn(sw, "failed to enable CL states\n"); if (tb_enable_tmu(sw)) tb_sw_warn(sw, "failed to enable TMU\n"); @@ -2022,20 +2029,13 @@ static int tb_suspend_noirq(struct tb *tb) static void tb_restore_children(struct tb_switch *sw) { struct tb_port *port; - int ret; /* No need to restore if the router is already unplugged */ if (sw->is_unplugged) return; - /* - * CL0s and CL1 are enabled and supported together. - * Silently ignore CLx re-enabling in case CLx is not supported. - */ - ret = tb_switch_clx_enable(sw, TB_CL1); - if (ret && ret != -EOPNOTSUPP) - tb_sw_warn(sw, "failed to re-enable %s on upstream port\n", - tb_switch_clx_name(TB_CL1)); + if (tb_enable_clx(sw)) + tb_sw_warn(sw, "failed to re-enable CL states\n"); if (tb_enable_tmu(sw)) tb_sw_warn(sw, "failed to restore TMU configuration\n"); -- cgit v1.2.3 From 35627353063bc145ba5c2c509dfe042982e2a219 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 10 Oct 2022 13:36:56 +0300 Subject: thunderbolt: Switch CL states from enum to a bitmask This is more natural and follows the hardware register layout better. This makes it easier to see which CL states we enable (even though they should be enabled together). Rename 'clx_mask' to 'clx' everywhere as this is now always bitmask. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/clx.c | 169 ++++++++++++++++++++++++------------------- drivers/thunderbolt/switch.c | 7 +- drivers/thunderbolt/tb.c | 2 +- drivers/thunderbolt/tb.h | 54 ++++---------- 4 files changed, 113 insertions(+), 119 deletions(-) diff --git a/drivers/thunderbolt/clx.c b/drivers/thunderbolt/clx.c index d1a502525425..4601607f1901 100644 --- a/drivers/thunderbolt/clx.c +++ b/drivers/thunderbolt/clx.c @@ -45,7 +45,7 @@ static int tb_port_pm_secondary_disable(struct tb_port *port) } /* Called for USB4 or Titan Ridge routers only */ -static bool tb_port_clx_supported(struct tb_port *port, unsigned int clx_mask) +static bool tb_port_clx_supported(struct tb_port *port, unsigned int clx) { u32 val, mask = 0; bool ret; @@ -65,11 +65,11 @@ static bool tb_port_clx_supported(struct tb_port *port, unsigned int clx_mask) return false; } - if (clx_mask & TB_CL1) { - /* CL0s and CL1 are enabled and supported together */ - mask |= LANE_ADP_CS_0_CL0S_SUPPORT | LANE_ADP_CS_0_CL1_SUPPORT; - } - if (clx_mask & TB_CL2) + if (clx & TB_CL0S) + mask |= LANE_ADP_CS_0_CL0S_SUPPORT; + if (clx & TB_CL1) + mask |= LANE_ADP_CS_0_CL1_SUPPORT; + if (clx & TB_CL2) mask |= LANE_ADP_CS_0_CL2_SUPPORT; ret = tb_port_read(port, &val, TB_CFG_PORT, @@ -80,16 +80,17 @@ static bool tb_port_clx_supported(struct tb_port *port, unsigned int clx_mask) return !!(val & mask); } -static int tb_port_clx_set(struct tb_port *port, enum tb_clx clx, bool enable) +static int tb_port_clx_set(struct tb_port *port, unsigned int clx, bool enable) { - u32 phy, mask; + u32 phy, mask = 0; int ret; - /* CL0s and CL1 are enabled and supported together */ - if (clx == TB_CL1) - mask = LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE; - else - /* For now we support only CL0s and CL1. Not CL2 */ + if (clx & TB_CL0S) + mask |= LANE_ADP_CS_1_CL0S_ENABLE; + if (clx & TB_CL1) + mask |= LANE_ADP_CS_1_CL1_ENABLE; + + if (!mask) return -EOPNOTSUPP; ret = tb_port_read(port, &phy, TB_CFG_PORT, @@ -106,12 +107,12 @@ static int tb_port_clx_set(struct tb_port *port, enum tb_clx clx, bool enable) port->cap_phy + LANE_ADP_CS_1, 1); } -static int tb_port_clx_disable(struct tb_port *port, enum tb_clx clx) +static int tb_port_clx_disable(struct tb_port *port, unsigned int clx) { return tb_port_clx_set(port, clx, false); } -static int tb_port_clx_enable(struct tb_port *port, enum tb_clx clx) +static int tb_port_clx_enable(struct tb_port *port, unsigned int clx) { return tb_port_clx_set(port, clx, true); } @@ -119,21 +120,23 @@ static int tb_port_clx_enable(struct tb_port *port, enum tb_clx clx) /** * tb_port_clx_is_enabled() - Is given CL state enabled * @port: USB4 port to check - * @clx_mask: Mask of CL states to check + * @clx: Mask of CL states to check * * Returns true if any of the given CL states is enabled for @port. */ -bool tb_port_clx_is_enabled(struct tb_port *port, unsigned int clx_mask) +bool tb_port_clx_is_enabled(struct tb_port *port, unsigned int clx) { u32 val, mask = 0; int ret; - if (!tb_port_clx_supported(port, clx_mask)) + if (!tb_port_clx_supported(port, clx)) return false; - if (clx_mask & TB_CL1) - mask |= LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE; - if (clx_mask & TB_CL2) + if (clx & TB_CL0S) + mask |= LANE_ADP_CS_1_CL0S_ENABLE; + if (clx & TB_CL1) + mask |= LANE_ADP_CS_1_CL1_ENABLE; + if (clx & TB_CL2) mask |= LANE_ADP_CS_1_CL2_ENABLE; ret = tb_port_read(port, &val, TB_CFG_PORT, @@ -205,6 +208,50 @@ static int tb_switch_mask_clx_objections(struct tb_switch *sw) sw->cap_lp + offset, ARRAY_SIZE(val)); } +/** + * tb_switch_clx_is_supported() - Is CLx supported on this type of router + * @sw: The router to check CLx support for + */ +bool tb_switch_clx_is_supported(const struct tb_switch *sw) +{ + if (!clx_enabled) + return false; + + if (sw->quirks & QUIRK_NO_CLX) + return false; + + /* + * CLx is not enabled and validated on Intel USB4 platforms + * before Alder Lake. + */ + if (tb_switch_is_tiger_lake(sw)) + return false; + + return tb_switch_is_usb4(sw) || tb_switch_is_titan_ridge(sw); +} + +static bool validate_mask(unsigned int clx) +{ + /* Previous states need to be enabled */ + if (clx & TB_CL2) + return (clx & (TB_CL0S | TB_CL1)) == (TB_CL0S | TB_CL1); + if (clx & TB_CL1) + return (clx & TB_CL0S) == TB_CL0S; + return true; +} + +static const char *clx_name(unsigned int clx) +{ + if (clx & TB_CL2) + return "CL0s/CL1/CL2"; + if (clx & TB_CL1) + return "CL0s/CL1"; + if (clx & TB_CL0S) + return "CL0s"; + + return "unknown"; +} + /** * tb_switch_clx_enable() - Enable CLx on upstream port of specified router * @sw: Router to enable CLx for @@ -219,46 +266,32 @@ static int tb_switch_mask_clx_objections(struct tb_switch *sw) * * Return: Returns 0 on success or an error code on failure. */ -int tb_switch_clx_enable(struct tb_switch *sw, enum tb_clx clx) +int tb_switch_clx_enable(struct tb_switch *sw, unsigned int clx) { - struct tb_switch *root_sw = sw->tb->root_switch; bool up_clx_support, down_clx_support; + struct tb_switch *parent_sw; struct tb_port *up, *down; int ret; - if (!clx_enabled) - return 0; + if (!validate_mask(clx)) + return -EINVAL; - /* - * CLx is not enabled and validated on Intel USB4 platforms before - * Alder Lake. - */ - if (root_sw->generation < 4 || tb_switch_is_tiger_lake(root_sw)) + parent_sw = tb_switch_parent(sw); + if (!parent_sw) return 0; - switch (clx) { - case TB_CL1: - /* CL0s and CL1 are enabled and supported together */ - break; - - default: - return -EOPNOTSUPP; - } - - if (!tb_switch_clx_is_supported(sw)) - return 0; - - /* - * Enable CLx for host router's downstream port as part of the - * downstream router enabling procedure. - */ - if (!tb_route(sw)) + if (!tb_switch_clx_is_supported(parent_sw) || + !tb_switch_clx_is_supported(sw)) return 0; /* Enable CLx only for first hop router (depth = 1) */ if (tb_route(tb_switch_parent(sw))) return 0; + /* CL2 is not yet supported */ + if (clx & TB_CL2) + return -EOPNOTSUPP; + ret = tb_switch_pm_secondary_resolve(sw); if (ret) return ret; @@ -269,9 +302,9 @@ int tb_switch_clx_enable(struct tb_switch *sw, enum tb_clx clx) up_clx_support = tb_port_clx_supported(up, clx); down_clx_support = tb_port_clx_supported(down, clx); - tb_port_dbg(up, "%s %ssupported\n", tb_switch_clx_name(clx), + tb_port_dbg(up, "%s %ssupported\n", clx_name(clx), up_clx_support ? "" : "not "); - tb_port_dbg(down, "%s %ssupported\n", tb_switch_clx_name(clx), + tb_port_dbg(down, "%s %ssupported\n", clx_name(clx), down_clx_support ? "" : "not "); if (!up_clx_support || !down_clx_support) @@ -294,52 +327,40 @@ int tb_switch_clx_enable(struct tb_switch *sw, enum tb_clx clx) return ret; } - sw->clx = clx; + sw->clx |= clx; - tb_port_dbg(up, "%s enabled\n", tb_switch_clx_name(clx)); + tb_port_dbg(up, "%s enabled\n", clx_name(clx)); return 0; } /** * tb_switch_clx_disable() - Disable CLx on upstream port of specified router * @sw: Router to disable CLx for - * @clx: The CLx state to disable + * + * Disables all CL states of the given router. Can be called on any + * router and if the states were not enabled already does nothing. * * Return: Returns 0 on success or an error code on failure. */ -int tb_switch_clx_disable(struct tb_switch *sw, enum tb_clx clx) +int tb_switch_clx_disable(struct tb_switch *sw) { + unsigned int clx = sw->clx; struct tb_port *up, *down; int ret; - if (!clx_enabled) - return 0; - - switch (clx) { - case TB_CL1: - /* CL0s and CL1 are enabled and supported together */ - break; - - default: - return -EOPNOTSUPP; - } - if (!tb_switch_clx_is_supported(sw)) return 0; - /* - * Disable CLx for host router's downstream port as part of the - * downstream router enabling procedure. - */ - if (!tb_route(sw)) - return 0; - /* Disable CLx only for first hop router (depth = 1) */ if (tb_route(tb_switch_parent(sw))) return 0; + if (!clx) + return 0; + up = tb_upstream_port(sw); down = tb_switch_downstream_port(sw); + ret = tb_port_clx_disable(up, clx); if (ret) return ret; @@ -348,8 +369,8 @@ int tb_switch_clx_disable(struct tb_switch *sw, enum tb_clx clx) if (ret) return ret; - sw->clx = TB_CLX_DISABLE; + sw->clx = 0; - tb_port_dbg(up, "%s disabled\n", tb_switch_clx_name(clx)); + tb_port_dbg(up, "%s disabled\n", clx_name(clx)); return 0; } diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 984b5536e143..f33a09d92c9b 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -3111,13 +3111,8 @@ void tb_switch_suspend(struct tb_switch *sw, bool runtime) /* * Actually only needed for Titan Ridge but for simplicity can be * done for USB4 device too as CLx is re-enabled at resume. - * CL0s and CL1 are enabled and supported together. */ - if (tb_switch_clx_is_enabled(sw, TB_CL1)) { - if (tb_switch_clx_disable(sw, TB_CL1)) - tb_sw_warn(sw, "failed to disable %s on upstream port\n", - tb_switch_clx_name(TB_CL1)); - } + tb_switch_clx_disable(sw); err = tb_plug_events_active(sw, false); if (err) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index e4f1233eb958..2d360508aeeb 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -248,7 +248,7 @@ static int tb_enable_clx(struct tb_switch *sw) * CL0s and CL1 are enabled and supported together. * Silently ignore CLx enabling in case CLx is not supported. */ - ret = tb_switch_clx_enable(sw, TB_CL1); + ret = tb_switch_clx_enable(sw, TB_CL0S | TB_CL1); return ret == -EOPNOTSUPP ? 0 : ret; } diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 23fbcc850c03..a223763e1272 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -117,13 +117,6 @@ struct tb_switch_tmu { enum tb_switch_tmu_rate rate_request; }; -enum tb_clx { - TB_CLX_DISABLE, - /* CL0s and CL1 are enabled and supported together */ - TB_CL1 = BIT(0), - TB_CL2 = BIT(1), -}; - /** * struct tb_switch - a thunderbolt switch * @dev: Device for the switch @@ -174,7 +167,7 @@ enum tb_clx { * @min_dp_main_credits: Router preferred minimum number of buffers for DP MAIN * @max_pcie_credits: Router preferred number of buffers for PCIe * @max_dma_credits: Router preferred number of buffers for DMA/P2P - * @clx: CLx state on the upstream link of the router + * @clx: CLx states on the upstream link of the router * * When the switch is being added or removed to the domain (other * switches) you need to have domain lock held. @@ -225,7 +218,7 @@ struct tb_switch { unsigned int min_dp_main_credits; unsigned int max_pcie_credits; unsigned int max_dma_credits; - enum tb_clx clx; + unsigned int clx; }; /** @@ -455,6 +448,11 @@ struct tb_path { #define TB_WAKE_ON_PCIE BIT(4) #define TB_WAKE_ON_DP BIT(5) +/* CL states */ +#define TB_CL0S BIT(0) +#define TB_CL1 BIT(1) +#define TB_CL2 BIT(2) + /** * struct tb_cm_ops - Connection manager specific operations vector * @driver_ready: Called right after control channel is started. Used by @@ -1002,46 +1000,26 @@ static inline bool tb_switch_tmu_is_enabled(const struct tb_switch *sw) sw->tmu.unidirectional == sw->tmu.unidirectional_request; } -bool tb_port_clx_is_enabled(struct tb_port *port, unsigned int clx_mask); - -static inline const char *tb_switch_clx_name(enum tb_clx clx) -{ - switch (clx) { - /* CL0s and CL1 are enabled and supported together */ - case TB_CL1: - return "CL0s/CL1"; - default: - return "unknown"; - } -} +bool tb_port_clx_is_enabled(struct tb_port *port, unsigned int clx); -int tb_switch_clx_enable(struct tb_switch *sw, enum tb_clx clx); -int tb_switch_clx_disable(struct tb_switch *sw, enum tb_clx clx); +bool tb_switch_clx_is_supported(const struct tb_switch *sw); +int tb_switch_clx_enable(struct tb_switch *sw, unsigned int clx); +int tb_switch_clx_disable(struct tb_switch *sw); /** * tb_switch_clx_is_enabled() - Checks if the CLx is enabled * @sw: Router to check for the CLx - * @clx: The CLx state to check for + * @clx: The CLx states to check for * * Checks if the specified CLx is enabled on the router upstream link. + * Returns true if any of the given states is enabled. + * * Not applicable for a host router. */ static inline bool tb_switch_clx_is_enabled(const struct tb_switch *sw, - enum tb_clx clx) + unsigned int clx) { - return sw->clx == clx; -} - -/** - * tb_switch_clx_is_supported() - Is CLx supported on this type of router - * @sw: The router to check CLx support for - */ -static inline bool tb_switch_clx_is_supported(const struct tb_switch *sw) -{ - if (sw->quirks & QUIRK_NO_CLX) - return false; - - return tb_switch_is_usb4(sw) || tb_switch_is_titan_ridge(sw); + return sw->clx & clx; } int tb_switch_pcie_l1_enable(struct tb_switch *sw); -- cgit v1.2.3 From 9650de737992cad35f48e19d7004a0ad2673dae1 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 18 Nov 2022 15:26:12 +0200 Subject: thunderbolt: Check for first depth router in tb.c Currently tb_switch_clx_enable() enables CL states only for the first depth router. This is something we may want to change in the future and in addition it is not visible from the calling path at all. For this reason do the check in the tb.c so it is immediately visible that we only do this for the first depth router. Fix the kernel-docs accordingly. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/clx.c | 22 ++++++---------------- drivers/thunderbolt/tb.c | 10 ++++++++++ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/thunderbolt/clx.c b/drivers/thunderbolt/clx.c index 4601607f1901..b8cfbd643311 100644 --- a/drivers/thunderbolt/clx.c +++ b/drivers/thunderbolt/clx.c @@ -257,14 +257,12 @@ static const char *clx_name(unsigned int clx) * @sw: Router to enable CLx for * @clx: The CLx state to enable * - * Enable CLx state only for first hop router. That is the most common - * use-case, that is intended for better thermal management, and so helps - * to improve performance. CLx is enabled only if both sides of the link - * support CLx, and if both sides of the link are not configured as two - * single lane links and only if the link is not inter-domain link. The - * complete set of conditions is described in CM Guide 1.0 section 8.1. + * CLx is enabled only if both sides of the link support CLx, and if both sides + * of the link are not configured as two single lane links and only if the link + * is not inter-domain link. The complete set of conditions is described in CM + * Guide 1.0 section 8.1. * - * Return: Returns 0 on success or an error code on failure. + * Returns %0 on success or an error code on failure. */ int tb_switch_clx_enable(struct tb_switch *sw, unsigned int clx) { @@ -284,10 +282,6 @@ int tb_switch_clx_enable(struct tb_switch *sw, unsigned int clx) !tb_switch_clx_is_supported(sw)) return 0; - /* Enable CLx only for first hop router (depth = 1) */ - if (tb_route(tb_switch_parent(sw))) - return 0; - /* CL2 is not yet supported */ if (clx & TB_CL2) return -EOPNOTSUPP; @@ -340,7 +334,7 @@ int tb_switch_clx_enable(struct tb_switch *sw, unsigned int clx) * Disables all CL states of the given router. Can be called on any * router and if the states were not enabled already does nothing. * - * Return: Returns 0 on success or an error code on failure. + * Returns %0 on success or an error code on failure. */ int tb_switch_clx_disable(struct tb_switch *sw) { @@ -351,10 +345,6 @@ int tb_switch_clx_disable(struct tb_switch *sw) if (!tb_switch_clx_is_supported(sw)) return 0; - /* Disable CLx only for first hop router (depth = 1) */ - if (tb_route(tb_switch_parent(sw))) - return 0; - if (!clx) return 0; diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 2d360508aeeb..1d056ff6d77f 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -244,6 +244,16 @@ static int tb_enable_clx(struct tb_switch *sw) { int ret; + /* + * Currently only enable CLx for the first link. This is enough + * to allow the CPU to save energy at least on Intel hardware + * and makes it slightly simpler to implement. We may change + * this in the future to cover the whole topology if it turns + * out to be beneficial. + */ + if (sw->config.depth != 1) + return 0; + /* * CL0s and CL1 are enabled and supported together. * Silently ignore CLx enabling in case CLx is not supported. -- cgit v1.2.3 From bdc6660e553ae74b2bf2689b69ee0b28d383c63f Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 10 Oct 2022 18:52:24 +0300 Subject: thunderbolt: Do not call CLx functions from TMU code There is really no need to call any of the CLx functions in the TMU code so remove all these checks. This makes the TMU enable/disable flows easier to follow as well. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tmu.c | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index 6988704c845c..7d06bacf24ff 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -383,14 +383,6 @@ out: */ int tb_switch_tmu_disable(struct tb_switch *sw) { - /* - * No need to disable TMU on devices that don't support CLx since - * on these devices e.g. Alpine Ridge and earlier, the TMU mode - * HiFi bi-directional is enabled by default and we don't change it. - */ - if (!tb_switch_clx_is_supported(sw)) - return 0; - /* Already disabled? */ if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF) return 0; @@ -648,25 +640,10 @@ int tb_switch_tmu_enable(struct tb_switch *sw) bool unidirectional = sw->tmu.unidirectional_request; int ret; - /* - * No need to enable TMU on devices that don't support CLx since on - * these devices e.g. Alpine Ridge and earlier, the TMU mode HiFi - * bi-directional is enabled by default. - */ - if (!tb_switch_clx_is_supported(sw)) - return 0; - if (tb_switch_tmu_is_enabled(sw)) return 0; if (tb_switch_is_titan_ridge(sw) && unidirectional) { - /* - * Titan Ridge supports CL0s and CL1 only. CL0s and CL1 are - * enabled and supported together. - */ - if (!tb_switch_clx_is_enabled(sw, TB_CL1)) - return -EOPNOTSUPP; - ret = tb_switch_tmu_disable_objections(sw); if (ret) return ret; -- cgit v1.2.3 From cb625ec6e57195a7b6a6d0947e6203614f874d3e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 26 May 2023 13:53:35 +0300 Subject: thunderbolt: Prefix TMU post time log message with "TMU: " Following what we do with other messages in this file. No functional changes. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index 7d06bacf24ff..c926fb71c43d 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -308,7 +308,7 @@ int tb_switch_tmu_post_time(struct tb_switch *sw) return ret; for (i = 0; i < ARRAY_SIZE(gm_local_time); i++) - tb_sw_dbg(root_switch, "local_time[%d]=0x%08x\n", i, + tb_sw_dbg(root_switch, "TMU: local_time[%d]=0x%08x\n", i, gm_local_time[i]); /* Convert to nanoseconds (drop fractional part) */ -- cgit v1.2.3 From b5d15961d95565934109b4cc53f50c3f5caf0d80 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 25 May 2023 12:22:11 +0300 Subject: thunderbolt: Prefix CL state related log messages with "CLx: " This makes it easier to spot from the logs and follows what we do with the TMU code already. We also log enabling/disabling CL states using the tb_sw_dbg() instead of tb_port_dbg(). Signed-off-by: Mika Westerberg --- drivers/thunderbolt/clx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/thunderbolt/clx.c b/drivers/thunderbolt/clx.c index b8cfbd643311..5e745386c413 100644 --- a/drivers/thunderbolt/clx.c +++ b/drivers/thunderbolt/clx.c @@ -296,9 +296,9 @@ int tb_switch_clx_enable(struct tb_switch *sw, unsigned int clx) up_clx_support = tb_port_clx_supported(up, clx); down_clx_support = tb_port_clx_supported(down, clx); - tb_port_dbg(up, "%s %ssupported\n", clx_name(clx), + tb_port_dbg(up, "CLx: %s %ssupported\n", clx_name(clx), up_clx_support ? "" : "not "); - tb_port_dbg(down, "%s %ssupported\n", clx_name(clx), + tb_port_dbg(down, "CLx: %s %ssupported\n", clx_name(clx), down_clx_support ? "" : "not "); if (!up_clx_support || !down_clx_support) @@ -323,7 +323,7 @@ int tb_switch_clx_enable(struct tb_switch *sw, unsigned int clx) sw->clx |= clx; - tb_port_dbg(up, "%s enabled\n", clx_name(clx)); + tb_sw_dbg(sw, "CLx: %s enabled\n", clx_name(clx)); return 0; } @@ -361,6 +361,6 @@ int tb_switch_clx_disable(struct tb_switch *sw) sw->clx = 0; - tb_port_dbg(up, "%s disabled\n", clx_name(clx)); + tb_sw_dbg(sw, "CLx: %s disabled\n", clx_name(clx)); return 0; } -- cgit v1.2.3 From 768e6fe69fde546beb942344c4c4932f3b3bb55b Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 24 May 2023 13:33:57 +0300 Subject: thunderbolt: Initialize CL states from the hardware In case the boot firmware enabled any of them, read the currently configured CL states and update the router structure accordingly. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/clx.c | 100 +++++++++++++++++++++++++++++++------------ drivers/thunderbolt/switch.c | 4 ++ drivers/thunderbolt/tb.h | 1 + 3 files changed, 78 insertions(+), 27 deletions(-) diff --git a/drivers/thunderbolt/clx.c b/drivers/thunderbolt/clx.c index 5e745386c413..960409df4405 100644 --- a/drivers/thunderbolt/clx.c +++ b/drivers/thunderbolt/clx.c @@ -15,6 +15,21 @@ static bool clx_enabled = true; module_param_named(clx, clx_enabled, bool, 0444); MODULE_PARM_DESC(clx, "allow low power states on the high-speed lanes (default: true)"); +static const char *clx_name(unsigned int clx) +{ + if (!clx) + return "disabled"; + + if (clx & TB_CL2) + return "CL0s/CL1/CL2"; + if (clx & TB_CL1) + return "CL0s/CL1"; + if (clx & TB_CL0S) + return "CL0s"; + + return "unknown"; +} + static int tb_port_pm_secondary_set(struct tb_port *port, bool secondary) { u32 phy; @@ -117,6 +132,29 @@ static int tb_port_clx_enable(struct tb_port *port, unsigned int clx) return tb_port_clx_set(port, clx, true); } +static int tb_port_clx(struct tb_port *port) +{ + u32 val; + int ret; + + if (!tb_port_clx_supported(port, TB_CL0S | TB_CL1 | TB_CL2)) + return 0; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_1, 1); + if (ret) + return ret; + + if (val & LANE_ADP_CS_1_CL0S_ENABLE) + ret |= TB_CL0S; + if (val & LANE_ADP_CS_1_CL1_ENABLE) + ret |= TB_CL1; + if (val & LANE_ADP_CS_1_CL2_ENABLE) + ret |= TB_CL2; + + return ret; +} + /** * tb_port_clx_is_enabled() - Is given CL state enabled * @port: USB4 port to check @@ -126,25 +164,45 @@ static int tb_port_clx_enable(struct tb_port *port, unsigned int clx) */ bool tb_port_clx_is_enabled(struct tb_port *port, unsigned int clx) { - u32 val, mask = 0; - int ret; + return !!(tb_port_clx(port) & clx); +} - if (!tb_port_clx_supported(port, clx)) - return false; +/** + * tb_switch_clx_init() - Initialize router CL states + * @sw: Router + * + * Can be called for any router. Initializes the current CL state by + * reading it from the hardware. + * + * Returns %0 in case of success and negative errno in case of failure. + */ +int tb_switch_clx_init(struct tb_switch *sw) +{ + struct tb_port *up, *down; + unsigned int clx, tmp; - if (clx & TB_CL0S) - mask |= LANE_ADP_CS_1_CL0S_ENABLE; - if (clx & TB_CL1) - mask |= LANE_ADP_CS_1_CL1_ENABLE; - if (clx & TB_CL2) - mask |= LANE_ADP_CS_1_CL2_ENABLE; + if (tb_switch_is_icm(sw)) + return 0; - ret = tb_port_read(port, &val, TB_CFG_PORT, - port->cap_phy + LANE_ADP_CS_1, 1); - if (ret) - return false; + if (!tb_route(sw)) + return 0; - return !!(val & mask); + if (!tb_switch_clx_is_supported(sw)) + return 0; + + up = tb_upstream_port(sw); + down = tb_switch_downstream_port(sw); + + clx = tb_port_clx(up); + tmp = tb_port_clx(down); + if (clx != tmp) + tb_sw_warn(sw, "CLx: inconsistent configuration %#x != %#x\n", + clx, tmp); + + tb_sw_dbg(sw, "CLx: current mode: %s\n", clx_name(clx)); + + sw->clx = clx; + return 0; } static int tb_switch_pm_secondary_resolve(struct tb_switch *sw) @@ -240,18 +298,6 @@ static bool validate_mask(unsigned int clx) return true; } -static const char *clx_name(unsigned int clx) -{ - if (clx & TB_CL2) - return "CL0s/CL1/CL2"; - if (clx & TB_CL1) - return "CL0s/CL1"; - if (clx & TB_CL0S) - return "CL0s"; - - return "unknown"; -} - /** * tb_switch_clx_enable() - Enable CLx on upstream port of specified router * @sw: Router to enable CLx for diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index f33a09d92c9b..0c11caec7e8e 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -2859,6 +2859,10 @@ int tb_switch_add(struct tb_switch *sw) if (ret) return ret; + ret = tb_switch_clx_init(sw); + if (ret) + return ret; + ret = tb_switch_tmu_init(sw); if (ret) return ret; diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index a223763e1272..2bda2816ff47 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1002,6 +1002,7 @@ static inline bool tb_switch_tmu_is_enabled(const struct tb_switch *sw) bool tb_port_clx_is_enabled(struct tb_port *port, unsigned int clx); +int tb_switch_clx_init(struct tb_switch *sw); bool tb_switch_clx_is_supported(const struct tb_switch *sw); int tb_switch_clx_enable(struct tb_switch *sw, unsigned int clx); int tb_switch_clx_disable(struct tb_switch *sw); -- cgit v1.2.3 From 4a420eb1426a237aaf105c9d040644785fc2c7fa Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 18 Nov 2022 15:42:27 +0200 Subject: thunderbolt: Make tb_switch_clx_disable() return CL states that were enabled This allows us to disable all CL states temporarily when running lane margining and then return back the previously enabled states. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/clx.c | 8 ++++++-- drivers/thunderbolt/debugfs.c | 35 ++++++++++++++++++++++++----------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/drivers/thunderbolt/clx.c b/drivers/thunderbolt/clx.c index 960409df4405..4f0cfbb24dd9 100644 --- a/drivers/thunderbolt/clx.c +++ b/drivers/thunderbolt/clx.c @@ -317,6 +317,9 @@ int tb_switch_clx_enable(struct tb_switch *sw, unsigned int clx) struct tb_port *up, *down; int ret; + if (!clx) + return 0; + if (!validate_mask(clx)) return -EINVAL; @@ -380,7 +383,8 @@ int tb_switch_clx_enable(struct tb_switch *sw, unsigned int clx) * Disables all CL states of the given router. Can be called on any * router and if the states were not enabled already does nothing. * - * Returns %0 on success or an error code on failure. + * Returns the CL states that were disabled or negative errno in case of + * failure. */ int tb_switch_clx_disable(struct tb_switch *sw) { @@ -408,5 +412,5 @@ int tb_switch_clx_disable(struct tb_switch *sw) sw->clx = 0; tb_sw_dbg(sw, "CLx: %s disabled\n", clx_name(clx)); - return 0; + return clx; } diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index e376ad25bf60..40b59e662ee3 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -553,8 +553,9 @@ static int margining_run_write(void *data, u64 val) struct usb4_port *usb4 = port->usb4; struct tb_switch *sw = port->sw; struct tb_margining *margining; + struct tb_switch *down_sw; struct tb *tb = sw->tb; - int ret; + int ret, clx; if (val != 1) return -EINVAL; @@ -566,15 +567,24 @@ static int margining_run_write(void *data, u64 val) goto out_rpm_put; } - /* - * CL states may interfere with lane margining so inform the user know - * and bail out. - */ - if (tb_port_clx_is_enabled(port, TB_CL1 | TB_CL2)) { - tb_port_warn(port, - "CL states are enabled, Disable them with clx=0 and re-connect\n"); - ret = -EINVAL; - goto out_unlock; + if (tb_is_upstream_port(port)) + down_sw = sw; + else if (port->remote) + down_sw = port->remote->sw; + else + down_sw = NULL; + + if (down_sw) { + /* + * CL states may interfere with lane margining so + * disable them temporarily now. + */ + ret = tb_switch_clx_disable(down_sw); + if (ret < 0) { + tb_sw_warn(down_sw, "failed to disable CL states\n"); + goto out_unlock; + } + clx = ret; } margining = usb4->margining; @@ -586,7 +596,7 @@ static int margining_run_write(void *data, u64 val) margining->right_high, USB4_MARGIN_SW_COUNTER_CLEAR); if (ret) - goto out_unlock; + goto out_clx; ret = usb4_port_sw_margin_errors(port, &margining->results[0]); } else { @@ -600,6 +610,9 @@ static int margining_run_write(void *data, u64 val) margining->right_high, margining->results); } +out_clx: + if (down_sw) + tb_switch_clx_enable(down_sw, clx); out_unlock: mutex_unlock(&tb->lock); out_rpm_put: -- cgit v1.2.3 From 53ba2e16957b01eb7858e3ee5a3a8187a2892f15 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 24 Mar 2023 13:04:39 +0200 Subject: thunderbolt: Disable CL states when a DMA tunnel is established Tunnels between hosts should not have CL states enabled because otherwise they might enter a low power state without the other end noticing which causes packets to be lost. For this reason disable all CL states upon first DMA tunnel creation. Once the last DMA tunnel is torn down we try to re-enable them. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/clx.c | 2 +- drivers/thunderbolt/tb.c | 62 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/drivers/thunderbolt/clx.c b/drivers/thunderbolt/clx.c index 4f0cfbb24dd9..604cceb23659 100644 --- a/drivers/thunderbolt/clx.c +++ b/drivers/thunderbolt/clx.c @@ -317,7 +317,7 @@ int tb_switch_clx_enable(struct tb_switch *sw, unsigned int clx) struct tb_port *up, *down; int ret; - if (!clx) + if (!clx || sw->clx == clx) return 0; if (!validate_mask(clx)) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 1d056ff6d77f..aa6e11589c28 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -240,8 +240,11 @@ static void tb_discover_dp_resources(struct tb *tb) } } +/* Enables CL states up to host router */ static int tb_enable_clx(struct tb_switch *sw) { + struct tb_cm *tcm = tb_priv(sw->tb); + const struct tb_tunnel *tunnel; int ret; /* @@ -251,9 +254,26 @@ static int tb_enable_clx(struct tb_switch *sw) * this in the future to cover the whole topology if it turns * out to be beneficial. */ + while (sw && sw->config.depth > 1) + sw = tb_switch_parent(sw); + + if (!sw) + return 0; + if (sw->config.depth != 1) return 0; + /* + * If we are re-enabling then check if there is an active DMA + * tunnel and in that case bail out. + */ + list_for_each_entry(tunnel, &tcm->tunnel_list, list) { + if (tb_tunnel_is_dma(tunnel)) { + if (tb_tunnel_port_on_path(tunnel, tb_upstream_port(sw))) + return 0; + } + } + /* * CL0s and CL1 are enabled and supported together. * Silently ignore CLx enabling in case CLx is not supported. @@ -262,6 +282,16 @@ static int tb_enable_clx(struct tb_switch *sw) return ret == -EOPNOTSUPP ? 0 : ret; } +/* Disables CL states up to the host router */ +static void tb_disable_clx(struct tb_switch *sw) +{ + do { + if (tb_switch_clx_disable(sw) < 0) + tb_sw_warn(sw, "failed to disable CL states\n"); + sw = tb_switch_parent(sw); + } while (sw); +} + static int tb_increase_switch_tmu_accuracy(struct device *dev, void *data) { struct tb_switch *sw; @@ -1470,30 +1500,45 @@ static int tb_approve_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, struct tb_port *nhi_port, *dst_port; struct tb_tunnel *tunnel; struct tb_switch *sw; + int ret; sw = tb_to_switch(xd->dev.parent); dst_port = tb_port_at(xd->route, sw); nhi_port = tb_switch_find_port(tb->root_switch, TB_TYPE_NHI); mutex_lock(&tb->lock); + + /* + * When tunneling DMA paths the link should not enter CL states + * so disable them now. + */ + tb_disable_clx(sw); + tunnel = tb_tunnel_alloc_dma(tb, nhi_port, dst_port, transmit_path, transmit_ring, receive_path, receive_ring); if (!tunnel) { - mutex_unlock(&tb->lock); - return -ENOMEM; + ret = -ENOMEM; + goto err_clx; } if (tb_tunnel_activate(tunnel)) { tb_port_info(nhi_port, "DMA tunnel activation failed, aborting\n"); - tb_tunnel_free(tunnel); - mutex_unlock(&tb->lock); - return -EIO; + ret = -EIO; + goto err_free; } list_add_tail(&tunnel->list, &tcm->tunnel_list); mutex_unlock(&tb->lock); return 0; + +err_free: + tb_tunnel_free(tunnel); +err_clx: + tb_enable_clx(sw); + mutex_unlock(&tb->lock); + + return ret; } static void __tb_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, @@ -1519,6 +1564,13 @@ static void __tb_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, receive_path, receive_ring)) tb_deactivate_and_free_tunnel(tunnel); } + + /* + * Try to re-enable CL states now, it is OK if this fails + * because we may still have another DMA tunnel active through + * the same host router USB4 downstream port. + */ + tb_enable_clx(sw); } static int tb_disconnect_xdomain_paths(struct tb *tb, struct tb_xdomain *xd, -- cgit v1.2.3 From 1402ba08abae5cfa583ff1a40b99c098a0532d41 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 26 May 2023 14:46:44 +0300 Subject: thunderbolt: Read retimer NVM authentication status prior tb_retimer_set_inbound_sbtx() According to the USB4 retimer guide the correct order is immediately after sending ENUMERATE_RETIMERS so update the code to follow this. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/retimer.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c index ccc2f0e7adba..fb8e113488db 100644 --- a/drivers/thunderbolt/retimer.c +++ b/drivers/thunderbolt/retimer.c @@ -187,6 +187,21 @@ static ssize_t nvm_authenticate_show(struct device *dev, return ret; } +static void tb_retimer_nvm_authenticate_status(struct tb_port *port, u32 *status) +{ + int i; + + tb_port_dbg(port, "reading NVM authentication status of retimers\n"); + + /* + * Before doing anything else, read the authentication status. + * If the retimer has it set, store it for the new retimer + * device instance. + */ + for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) + usb4_port_retimer_nvm_authenticate_status(port, i, &status[i]); +} + static void tb_retimer_set_inbound_sbtx(struct tb_port *port) { int i; @@ -449,18 +464,16 @@ int tb_retimer_scan(struct tb_port *port, bool add) return ret; /* - * Enable sideband channel for each retimer. We can do this - * regardless whether there is device connected or not. + * Immediately after sending enumerate retimers read the + * authentication status of each retimer. */ - tb_retimer_set_inbound_sbtx(port); + tb_retimer_nvm_authenticate_status(port, status); /* - * Before doing anything else, read the authentication status. - * If the retimer has it set, store it for the new retimer - * device instance. + * Enable sideband channel for each retimer. We can do this + * regardless whether there is device connected or not. */ - for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) - usb4_port_retimer_nvm_authenticate_status(port, i, &status[i]); + tb_retimer_set_inbound_sbtx(port); for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) { /* -- cgit v1.2.3 From b7b83911f7906a72f50806279ef22c86be728b69 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 26 May 2023 14:51:23 +0300 Subject: thunderbolt: Do not send UNSET_INBOUND_SBTX when retimer NVM authentication started Once retimer NVM authentication is started, sending UNSET_INBOUND_SBTX will fail so avoid doing that. Only send it when we are writing an image with not authentication or when the authentication failed early. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/retimer.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c index fb8e113488db..a273fb02a02c 100644 --- a/drivers/thunderbolt/retimer.c +++ b/drivers/thunderbolt/retimer.c @@ -244,6 +244,13 @@ static ssize_t nvm_authenticate_store(struct device *dev, rt->auth_status = 0; if (val) { + /* + * When NVM authentication starts the retimer is not + * accessible so calling tb_retimer_unset_inbound_sbtx() + * will fail and therefore we do not call it. Exception + * is when the validation fails or we only write the new + * NVM image without authentication. + */ tb_retimer_set_inbound_sbtx(rt->port); if (val == AUTHENTICATE_ONLY) { ret = tb_retimer_nvm_authenticate(rt, true); @@ -264,7 +271,8 @@ static ssize_t nvm_authenticate_store(struct device *dev, } exit_unlock: - tb_retimer_unset_inbound_sbtx(rt->port); + if (ret || val == WRITE_ONLY) + tb_retimer_unset_inbound_sbtx(rt->port); mutex_unlock(&rt->tb->lock); exit_rpm: pm_runtime_mark_last_busy(&rt->dev); -- cgit v1.2.3 From 87200371817ec6e48e42ef2f03756268661a8815 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 26 May 2023 14:55:20 +0300 Subject: thunderbolt: Enable/disable sideband depending on USB4 port offline mode When USB4 port is in offline mode (this mean there is no device attached) we want to keep the sideband up to make it possible to communicate with the retimers. In the same way there is no need to enable sideband transactions when the USB4 port is not offline as they are already up. For this reason make the enabling/disabling depend on the USB4 port offline status. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/retimer.c | 19 +++++++++++++++++++ drivers/thunderbolt/tb.h | 5 +++++ 2 files changed, 24 insertions(+) diff --git a/drivers/thunderbolt/retimer.c b/drivers/thunderbolt/retimer.c index a273fb02a02c..47becb363ada 100644 --- a/drivers/thunderbolt/retimer.c +++ b/drivers/thunderbolt/retimer.c @@ -206,6 +206,15 @@ static void tb_retimer_set_inbound_sbtx(struct tb_port *port) { int i; + /* + * When USB4 port is online sideband communications are + * already up. + */ + if (!usb4_port_device_is_offline(port->usb4)) + return; + + tb_port_dbg(port, "enabling sideband transactions\n"); + for (i = 1; i <= TB_MAX_RETIMER_INDEX; i++) usb4_port_retimer_set_inbound_sbtx(port, i); } @@ -214,6 +223,16 @@ static void tb_retimer_unset_inbound_sbtx(struct tb_port *port) { int i; + /* + * When USB4 port is offline we need to keep the sideband + * communications up to make it possible to communicate with + * the connected retimers. + */ + if (usb4_port_device_is_offline(port->usb4)) + return; + + tb_port_dbg(port, "disabling sideband transactions\n"); + for (i = TB_MAX_RETIMER_INDEX; i >= 1; i--) usb4_port_retimer_unset_inbound_sbtx(port, i); } diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 2bda2816ff47..58df106aaa5e 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1301,6 +1301,11 @@ struct usb4_port *usb4_port_device_add(struct tb_port *port); void usb4_port_device_remove(struct usb4_port *usb4); int usb4_port_device_resume(struct usb4_port *usb4); +static inline bool usb4_port_device_is_offline(const struct usb4_port *usb4) +{ + return usb4->offline; +} + void tb_check_quirks(struct tb_switch *sw); #ifdef CONFIG_ACPI -- cgit v1.2.3 From b47ad02ff283d60e55e3e59d0e2455e3ab1812c6 Mon Sep 17 00:00:00 2001 From: Udipto Goswami Date: Fri, 2 Jun 2023 17:39:59 +0300 Subject: usb: xhci: Remove unused udev from xhci_log_ctx trace event xhci_log_ctx event is not utilizing the extracted udev to print out anything, hence removing it. Fixes: 1d27fabec068 ("xhci: add xhci_address_ctx trace event") Signed-off-by: Udipto Goswami Signed-off-by: Mathias Nyman Message-ID: <20230602144009.1225632-2-mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-trace.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 4286dba5b157..7555c4ea7c4b 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -80,20 +80,16 @@ DECLARE_EVENT_CLASS(xhci_log_ctx, __field(dma_addr_t, ctx_dma) __field(u8 *, ctx_va) __field(unsigned, ctx_ep_num) - __field(int, slot_id) __dynamic_array(u32, ctx_data, ((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 8) * ((ctx->type == XHCI_CTX_TYPE_INPUT) + ep_num + 1)) ), TP_fast_assign( - struct usb_device *udev; - udev = to_usb_device(xhci_to_hcd(xhci)->self.controller); __entry->ctx_64 = HCC_64BYTE_CONTEXT(xhci->hcc_params); __entry->ctx_type = ctx->type; __entry->ctx_dma = ctx->dma; __entry->ctx_va = ctx->bytes; - __entry->slot_id = udev->slot_id; __entry->ctx_ep_num = ep_num; memcpy(__get_dynamic_array(ctx_data), ctx->bytes, ((HCC_64BYTE_CONTEXT(xhci->hcc_params) + 1) * 32) * -- cgit v1.2.3 From b9e43779ac9be64da453d67f550fc46c335791e1 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 2 Jun 2023 17:40:00 +0300 Subject: xhci: Add usb cold attach (CAS) as a reason to resume root hub. Check for the cold attach (CAS) bit while checking for other usb3 roothub port changes during host resume. The CAS bit is set if a USB 3 device is connected while the host is suspended in such a way it can't perform proper link training and progress the link to the enabled U0 state. If the CAS bit set we want to resume the root hub, and reset and enumerate the newly connected device. Signed-off-by: Mathias Nyman Message-ID: <20230602144009.1225632-3-mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index b81313ffeb76..3a13e2453203 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -833,7 +833,7 @@ static bool xhci_pending_portevent(struct xhci_hcd *xhci) ports = xhci->usb3_rhub.ports; while (port_index--) { portsc = readl(ports[port_index]->addr); - if (portsc & PORT_CHANGE_MASK || + if (portsc & (PORT_CHANGE_MASK | PORT_CAS) || (portsc & PORT_PLS_MASK) == XDEV_RESUME) return true; } -- cgit v1.2.3 From 9b907c91aa94522ae14bf155ce7b9ccb10a0903c Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 2 Jun 2023 17:40:01 +0300 Subject: xhci: Don't require a valid get_quirks() function pointer during xhci setup Not all platforms drivers need to set up custom quirks during the xhci generic setup. Allow them to pass NULL as the function pointer when calling xhci_gen_setup() Signed-off-by: Mathias Nyman Message-ID: <20230602144009.1225632-4-mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 3a13e2453203..176969bf2d5c 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -5181,7 +5181,8 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) xhci->quirks |= quirks; - get_quirks(dev, xhci); + if (get_quirks) + get_quirks(dev, xhci); /* In xhci controllers which follow xhci 1.0 spec gives a spurious * success event after a short transfer. This quirk will ignore such -- cgit v1.2.3 From 0a4776205b16d038ec6fedef2094951fcb6f441b Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 2 Jun 2023 17:40:02 +0300 Subject: xhci: get rid of XHCI_PLAT quirk that used to prevent MSI setup The XHCI_PLAT quirk was only needed to ensure non-PCI xHC host avoided setting up MSI interrupts in generic xhci codepaths. The MSI setup code is now moved to PCI specific xhci-pci.c file so the quirk is no longer needed. Remove setting the XHCI_PLAT quirk for HiSilocon SoC xHC, NVIDIA Tegra xHC, MediaTek xHC, the generic xhci-plat driver, and the checks for XHCI_PLAT in xhci-pci.c MSI setup code. Signed-off-by: Mathias Nyman Message-ID: <20230602144009.1225632-5-mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-histb.c | 12 +----------- drivers/usb/host/xhci-mtk.c | 6 ------ drivers/usb/host/xhci-pci.c | 7 ------- drivers/usb/host/xhci-plat.c | 7 +------ drivers/usb/host/xhci-tegra.c | 1 - drivers/usb/host/xhci.h | 2 +- 6 files changed, 3 insertions(+), 32 deletions(-) diff --git a/drivers/usb/host/xhci-histb.c b/drivers/usb/host/xhci-histb.c index d8aba07e802d..f9a4a4b0eb57 100644 --- a/drivers/usb/host/xhci-histb.c +++ b/drivers/usb/host/xhci-histb.c @@ -164,16 +164,6 @@ static void xhci_histb_host_disable(struct xhci_hcd_histb *histb) clk_disable_unprepare(histb->bus_clk); } -static void xhci_histb_quirks(struct device *dev, struct xhci_hcd *xhci) -{ - /* - * As of now platform drivers don't provide MSI support so we ensure - * here that the generic code does not try to make a pci_dev from our - * dev struct in order to setup MSI - */ - xhci->quirks |= XHCI_PLAT; -} - /* called during probe() after chip reset completes */ static int xhci_histb_setup(struct usb_hcd *hcd) { @@ -186,7 +176,7 @@ static int xhci_histb_setup(struct usb_hcd *hcd) return ret; } - return xhci_gen_setup(hcd, xhci_histb_quirks); + return xhci_gen_setup(hcd, NULL); } static const struct xhci_driver_overrides xhci_histb_overrides __initconst = { diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index 8d9a55b0281b..51d9d4d4f6a5 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -418,12 +418,6 @@ static void xhci_mtk_quirks(struct device *dev, struct xhci_hcd *xhci) struct usb_hcd *hcd = xhci_to_hcd(xhci); struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd); - /* - * As of now platform drivers don't provide MSI support so we ensure - * here that the generic code does not try to make a pci_dev from our - * dev struct in order to setup MSI - */ - xhci->quirks |= XHCI_PLAT; xhci->quirks |= XHCI_MTK_HOST; /* * MTK host controller gives a spurious successful event after a diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 69a5cb7eba38..611703f863e0 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -108,9 +108,6 @@ static void xhci_cleanup_msix(struct xhci_hcd *xhci) struct usb_hcd *hcd = xhci_to_hcd(xhci); struct pci_dev *pdev = to_pci_dev(hcd->self.controller); - if (xhci->quirks & XHCI_PLAT) - return; - /* return if using legacy interrupt */ if (hcd->irq > 0) return; @@ -208,10 +205,6 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd) struct pci_dev *pdev; int ret; - /* The xhci platform device has set up IRQs through usb_add_hcd. */ - if (xhci->quirks & XHCI_PLAT) - return 0; - pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller); /* * Some Fresco Logic host controllers advertise MSI, but fail to diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index a52d73c2cd80..1d902d1513bc 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -78,12 +78,7 @@ static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) { struct xhci_plat_priv *priv = xhci_to_priv(xhci); - /* - * As of now platform drivers don't provide MSI support so we ensure - * here that the generic code does not try to make a pci_dev from our - * dev struct in order to setup MSI - */ - xhci->quirks |= XHCI_PLAT | priv->quirks; + xhci->quirks |= priv->quirks; } /* called during probe() after chip reset completes */ diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index f124483044a2..6ca8a37e53e1 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -2663,7 +2663,6 @@ static void tegra_xhci_quirks(struct device *dev, struct xhci_hcd *xhci) { struct tegra_xusb *tegra = dev_get_drvdata(dev); - xhci->quirks |= XHCI_PLAT; if (tegra && tegra->soc->lpm_support) xhci->quirks |= XHCI_LPM_SUPPORT; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index f845c15073ba..56e318c384ff 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1874,7 +1874,7 @@ struct xhci_hcd { #define XHCI_SPURIOUS_REBOOT BIT_ULL(13) #define XHCI_COMP_MODE_QUIRK BIT_ULL(14) #define XHCI_AVOID_BEI BIT_ULL(15) -#define XHCI_PLAT BIT_ULL(16) +#define XHCI_PLAT BIT_ULL(16) /* Deprecated */ #define XHCI_SLOW_SUSPEND BIT_ULL(17) #define XHCI_SPURIOUS_WAKEUP BIT_ULL(18) /* For controllers with a broken beyond repair streams implementation */ -- cgit v1.2.3 From 4bf398e15aa410e25fa46a5684c7e5bf682ecc48 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 2 Jun 2023 17:40:03 +0300 Subject: xhci: split allocate interrupter into separate alloacte and add parts The current function that both allocates and adds the interrupter isn't optimal when using several interrupters. The array of interrupters need to be protected with a lock while adding or removing interrupters. If memory is allocated under the default xhci spinlock then GFP_KERNEL can't be used. There is no need to allocate the interrupter memory under the lock, so split this code into separate unlocked allocate part, and a lock protected add part. Signed-off-by: Mathias Nyman Message-ID: <20230602144009.1225632-6-mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 78 ++++++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 7e106bd804ca..2bf8121f4d36 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1831,13 +1831,15 @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir) * low or high 32 bits of ERSTBA immediately causes the controller to * dereference the partially cleared 64 bit address, causing IOMMU error. */ - tmp = readl(&ir->ir_set->erst_size); - tmp &= ERST_SIZE_MASK; - writel(tmp, &ir->ir_set->erst_size); - - tmp64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); - tmp64 &= (u64) ERST_PTR_MASK; - xhci_write_64(xhci, tmp64, &ir->ir_set->erst_dequeue); + if (ir->ir_set) { + tmp = readl(&ir->ir_set->erst_size); + tmp &= ERST_SIZE_MASK; + writel(tmp, &ir->ir_set->erst_size); + + tmp64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue); + tmp64 &= (u64) ERST_PTR_MASK; + xhci_write_64(xhci, tmp64, &ir->ir_set->erst_dequeue); + } /* free interrrupter event ring */ if (ir->event_ring) @@ -2227,43 +2229,50 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) } static struct xhci_interrupter * -xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int intr_num, gfp_t flags) +xhci_alloc_interrupter(struct xhci_hcd *xhci, gfp_t flags) { struct device *dev = xhci_to_hcd(xhci)->self.sysdev; struct xhci_interrupter *ir; - u64 erst_base; - u32 erst_size; int ret; - if (intr_num > xhci->max_interrupters) { - xhci_warn(xhci, "Can't allocate interrupter %d, max interrupters %d\n", - intr_num, xhci->max_interrupters); - return NULL; - } - - if (xhci->interrupter) { - xhci_warn(xhci, "Can't allocate already set up interrupter %d\n", intr_num); - return NULL; - } - ir = kzalloc_node(sizeof(*ir), flags, dev_to_node(dev)); if (!ir) return NULL; - ir->ir_set = &xhci->run_regs->ir_set[intr_num]; ir->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS, 1, TYPE_EVENT, 0, flags); if (!ir->event_ring) { - xhci_warn(xhci, "Failed to allocate interrupter %d event ring\n", intr_num); - goto fail_ir; + xhci_warn(xhci, "Failed to allocate interrupter event ring\n"); + kfree(ir); + return NULL; } ret = xhci_alloc_erst(xhci, ir->event_ring, &ir->erst, flags); if (ret) { - xhci_warn(xhci, "Failed to allocate interrupter %d erst\n", intr_num); - goto fail_ev; + xhci_warn(xhci, "Failed to allocate interrupter erst\n"); + xhci_ring_free(xhci, ir->event_ring); + kfree(ir); + return NULL; + } + + return ir; +} + +static int +xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir, + unsigned int intr_num) +{ + u64 erst_base; + u32 erst_size; + if (intr_num > xhci->max_interrupters) { + xhci_warn(xhci, "Can't add interrupter %d, max interrupters %d\n", + intr_num, xhci->max_interrupters); + return -EINVAL; } + + ir->ir_set = &xhci->run_regs->ir_set[intr_num]; + /* set ERST count with the number of entries in the segment table */ erst_size = readl(&ir->ir_set->erst_size); erst_size &= ERST_SIZE_MASK; @@ -2278,14 +2287,7 @@ xhci_alloc_interrupter(struct xhci_hcd *xhci, unsigned int intr_num, gfp_t flags /* Set the event ring dequeue address of this interrupter */ xhci_set_hc_event_deq(xhci, ir); - return ir; - -fail_ev: - xhci_ring_free(xhci, ir->event_ring); -fail_ir: - kfree(ir); - - return NULL; + return 0; } int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) @@ -2407,15 +2409,17 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) "// Doorbell array is located at offset 0x%x from cap regs base addr", val); xhci->dba = (void __iomem *) xhci->cap_regs + val; - /* Set ir_set to interrupt register set 0 */ - /* allocate and set up primary interrupter with an event ring. */ + /* Allocate and set up primary interrupter 0 with an event ring. */ xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Allocating primary event ring"); - xhci->interrupter = xhci_alloc_interrupter(xhci, 0, flags); + xhci->interrupter = xhci_alloc_interrupter(xhci, flags); if (!xhci->interrupter) goto fail; + if (xhci_add_interrupter(xhci, xhci->interrupter, 0)) + goto fail; + xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX; /* -- cgit v1.2.3 From f5af638f0609af889f15c700c60b93c06cc76675 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 2 Jun 2023 17:40:04 +0300 Subject: xhci: Fix transfer ring expansion size calculation The amount of new TRBs needed is calculated incorrectly when expanding a transfer ring. The room_on_ring() helper will correctly report that the ring needs expansion if the enqueue pointer is about to reach the dequeue segment. If enqueue reaches the dequeue segment then there is no easy way to expand the ring by adding new segments between enqueue and dequeue. This leads to ring expansion even if num_trbs_free is larger than num_trbs we are queueing. As a result we try to store a negative number in a unsigned int, leading to a huge percieved trb need, and doubling of ring size. Rework and rename the room_on_ring() to a helper that checks if ring needs expansion, and return number of new segments needed. Don't rely on the tracked ring->num_trbs_free value as turns out it has been unreliable. Use ring enqueue and dequeue positions to determine expansion need. The unsigned int issue was first reported first Chao zeng, and a bit later seen in a real world bug. Reported-by: chao zeng Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217242 Tested-by: Miller Hunter Signed-off-by: Mathias Nyman Message-ID: <20230602144009.1225632-7-mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 14 +++------- drivers/usb/host/xhci-ring.c | 64 +++++++++++++++++++++++++++++--------------- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 2bf8121f4d36..c6f3ef2334a3 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -422,22 +422,14 @@ void xhci_free_endpoint_ring(struct xhci_hcd *xhci, * Allocate a new ring which has same segment numbers and link the two rings. */ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, - unsigned int num_trbs, gfp_t flags) + unsigned int num_new_segs, gfp_t flags) { struct xhci_segment *first; struct xhci_segment *last; - unsigned int num_segs; - unsigned int num_segs_needed; int ret; - num_segs_needed = (num_trbs + (TRBS_PER_SEGMENT - 1) - 1) / - (TRBS_PER_SEGMENT - 1); - - /* Allocate number of segments we needed, or double the ring size */ - num_segs = max(ring->num_segs, num_segs_needed); - ret = xhci_alloc_segments_for_ring(xhci, &first, &last, - num_segs, ring->cycle_state, ring->type, + num_new_segs, ring->cycle_state, ring->type, ring->bounce_buf_len, flags); if (ret) return -ENOMEM; @@ -457,7 +449,7 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring, return ret; } - xhci_link_rings(xhci, ring, first, last, num_segs); + xhci_link_rings(xhci, ring, first, last, num_new_segs); trace_xhci_ring_expansion(ring); xhci_dbg_trace(xhci, trace_xhci_dbg_ring_expansion, "ring expansion succeed, now has %d segments", diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 2bc82b3a2f98..2722dca6218e 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -299,22 +299,45 @@ static int xhci_num_trbs_to(struct xhci_segment *start_seg, union xhci_trb *star /* * Check to see if there's room to enqueue num_trbs on the ring and make sure * enqueue pointer will not advance into dequeue segment. See rules above. + * return number of new segments needed to ensure this. */ -static inline int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring, - unsigned int num_trbs) + +static unsigned int xhci_ring_expansion_needed(struct xhci_hcd *xhci, struct xhci_ring *ring, + unsigned int num_trbs) { - int num_trbs_in_deq_seg; + struct xhci_segment *seg; + int trbs_past_seg; + int enq_used; + int new_segs; + + enq_used = ring->enqueue - ring->enq_seg->trbs; - if (ring->num_trbs_free < num_trbs) + /* how many trbs will be queued past the enqueue segment? */ + trbs_past_seg = enq_used + num_trbs - (TRBS_PER_SEGMENT - 1); + + if (trbs_past_seg <= 0) return 0; - if (ring->type != TYPE_COMMAND && ring->type != TYPE_EVENT) { - num_trbs_in_deq_seg = ring->dequeue - ring->deq_seg->trbs; - if (ring->num_trbs_free < num_trbs + num_trbs_in_deq_seg) - return 0; + /* Empty ring special case, enqueue stuck on link trb while dequeue advanced */ + if (trb_is_link(ring->enqueue) && ring->enq_seg->next->trbs == ring->dequeue) + return 0; + + new_segs = 1 + (trbs_past_seg / (TRBS_PER_SEGMENT - 1)); + seg = ring->enq_seg; + + while (new_segs > 0) { + seg = seg->next; + if (seg == ring->deq_seg) { + xhci_dbg(xhci, "Ring expansion by %d segments needed\n", + new_segs); + xhci_dbg(xhci, "Adding %d trbs moves enq %d trbs into deq seg\n", + num_trbs, trbs_past_seg % TRBS_PER_SEGMENT); + return new_segs; + } + new_segs--; } - return 1; + return 0; } /* Ring the host controller doorbell after placing a command on the ring */ @@ -3165,13 +3188,13 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, /* * Does various checks on the endpoint ring, and makes it ready to queue num_trbs. - * FIXME allocate segments if the ring is full. + * expand ring if it start to be full. */ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, u32 ep_state, unsigned int num_trbs, gfp_t mem_flags) { - unsigned int num_trbs_needed; unsigned int link_trb_count = 0; + unsigned int new_segs = 0; /* Make sure the endpoint has been added to xHC schedule */ switch (ep_state) { @@ -3202,20 +3225,17 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, return -EINVAL; } - while (1) { - if (room_on_ring(xhci, ep_ring, num_trbs)) - break; - - if (ep_ring == xhci->cmd_ring) { - xhci_err(xhci, "Do not support expand command ring\n"); - return -ENOMEM; - } + if (ep_ring != xhci->cmd_ring) { + new_segs = xhci_ring_expansion_needed(xhci, ep_ring, num_trbs); + } else if (ep_ring->num_trbs_free <= num_trbs) { + xhci_err(xhci, "Do not support expand command ring\n"); + return -ENOMEM; + } + if (new_segs) { xhci_dbg_trace(xhci, trace_xhci_dbg_ring_expansion, "ERROR no room on ep ring, try ring expansion"); - num_trbs_needed = num_trbs - ep_ring->num_trbs_free; - if (xhci_ring_expansion(xhci, ep_ring, num_trbs_needed, - mem_flags)) { + if (xhci_ring_expansion(xhci, ep_ring, new_segs, mem_flags)) { xhci_err(xhci, "Ring expansion failed\n"); return -ENOMEM; } -- cgit v1.2.3 From 2710f8186f889798f7d1b87b762461a07cac56c6 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Fri, 2 Jun 2023 17:40:05 +0300 Subject: xhci: Stop unnecessary tracking of free trbs in a ring Trying to keep track of free trbs in a ring by adding and subtracting deltas each time a enqueue or dequeue is increased or moved has proven to be buggy and complicated, especially over long periods of time. Recently a bug in counting free trbs was fixed, now taking into account cancelled URBs that were turned into no-ops, preventing free_trbs to slowly wander off causing unnecessary ring expansion. See commit fe82f16aafda ("xhci: Fix incorrect tracking of free space on transfer rings") Turns out its a lot easier to just calculate the numer of free TRB based on ring size and the current enqueue and dequeue pointer values. This is currently only needed for the command ring as multi segment transfer rings already ensures there is enough room the ring during the ring expansion check. We could get rid of the ring->num_trbs_free entry completely, but as the xhci DbC code also uses it we don't clean that up in this patch. Reported-by: Miller Hunter Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217242 Tested-by: Miller Hunter Signed-off-by: Mathias Nyman Message-ID: <20230602144009.1225632-8-mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 1 - drivers/usb/host/xhci-ring.c | 75 +++++++++++++++++++------------------------ drivers/usb/host/xhci-trace.h | 5 +-- drivers/usb/host/xhci.h | 3 +- 4 files changed, 35 insertions(+), 49 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index c6f3ef2334a3..a8e9a4bb1537 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -143,7 +143,6 @@ static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring, xhci_link_segments(ring->enq_seg, first, ring->type, chain_links); xhci_link_segments(last, next, ring->type, chain_links); ring->num_segs += num_segs; - ring->num_trbs_free += (TRBS_PER_SEGMENT - 1) * num_segs; if (ring->type != TYPE_EVENT && ring->enq_seg == ring->last_seg) { ring->last_seg->trbs[TRBS_PER_SEGMENT-1].link.control diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 2722dca6218e..646ff125def5 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -174,12 +174,10 @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring) /* All other rings have link trbs */ if (!trb_is_link(ring->dequeue)) { - if (last_trb_on_seg(ring->deq_seg, ring->dequeue)) { + if (last_trb_on_seg(ring->deq_seg, ring->dequeue)) xhci_warn(xhci, "Missing link TRB at end of segment\n"); - } else { + else ring->dequeue++; - ring->num_trbs_free++; - } } while (trb_is_link(ring->dequeue)) { @@ -221,9 +219,6 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, unsigned int link_trb_count = 0; chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN; - /* If this is not event ring, there is one less usable TRB */ - if (!trb_is_link(ring->enqueue)) - ring->num_trbs_free--; if (last_trb_on_seg(ring->enq_seg, ring->enqueue)) { xhci_err(xhci, "Tried to move enqueue past ring segment\n"); @@ -276,24 +271,40 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, trace_xhci_inc_enq(ring); } -static int xhci_num_trbs_to(struct xhci_segment *start_seg, union xhci_trb *start, - struct xhci_segment *end_seg, union xhci_trb *end, - unsigned int num_segs) +/* + * Return number of free normal TRBs from enqueue to dequeue pointer on ring. + * Not counting an assumed link TRB at end of each TRBS_PER_SEGMENT sized segment. + * 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) { + struct xhci_segment *enq_seg = ring->enq_seg; + union xhci_trb *enq = ring->enqueue; union xhci_trb *last_on_seg; - int num = 0; + unsigned int free = 0; int i = 0; + /* Ring might be empty even if enq != deq if enq is left on a link trb */ + if (trb_is_link(enq)) { + enq_seg = enq_seg->next; + enq = enq_seg->trbs; + } + + /* Empty ring, common case, don't walk the segments */ + if (enq == ring->dequeue) + return ring->num_segs * (TRBS_PER_SEGMENT - 1); + do { - if (start_seg == end_seg && end >= start) - return num + (end - start); - last_on_seg = &start_seg->trbs[TRBS_PER_SEGMENT - 1]; - num += last_on_seg - start; - start_seg = start_seg->next; - start = start_seg->trbs; - } while (i++ <= num_segs); - - return -EINVAL; + if (ring->deq_seg == enq_seg && ring->dequeue >= enq) + return free + (ring->dequeue - enq); + last_on_seg = &enq_seg->trbs[TRBS_PER_SEGMENT - 1]; + free += last_on_seg - enq; + enq_seg = enq_seg->next; + enq = enq_seg->trbs; + } while (i++ <= ring->num_segs); + + return free; } /* @@ -1291,10 +1302,7 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci, unsigned int ep_index) { union xhci_trb *dequeue_temp; - int num_trbs_free_temp; - bool revert = false; - num_trbs_free_temp = ep_ring->num_trbs_free; dequeue_temp = ep_ring->dequeue; /* If we get two back-to-back stalls, and the first stalled transfer @@ -1310,7 +1318,6 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci, while (ep_ring->dequeue != dev->eps[ep_index].queued_deq_ptr) { /* We have more usable TRBs */ - ep_ring->num_trbs_free++; ep_ring->dequeue++; if (trb_is_link(ep_ring->dequeue)) { if (ep_ring->dequeue == @@ -1320,15 +1327,10 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci, ep_ring->dequeue = ep_ring->deq_seg->trbs; } if (ep_ring->dequeue == dequeue_temp) { - revert = true; + xhci_dbg(xhci, "Unable to find new dequeue pointer\n"); break; } } - - if (revert) { - xhci_dbg(xhci, "Unable to find new dequeue pointer\n"); - ep_ring->num_trbs_free = num_trbs_free_temp; - } } /* @@ -2183,7 +2185,6 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, u32 trb_comp_code) { struct xhci_ep_ctx *ep_ctx; - int trbs_freed; ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep->ep_index); @@ -2253,13 +2254,6 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, } /* Update ring dequeue pointer */ - trbs_freed = xhci_num_trbs_to(ep_ring->deq_seg, ep_ring->dequeue, - td->last_trb_seg, td->last_trb, - ep_ring->num_segs); - if (trbs_freed < 0) - xhci_dbg(xhci, "Failed to count freed trbs at TD finish\n"); - else - ep_ring->num_trbs_free += trbs_freed; ep_ring->dequeue = td->last_trb; ep_ring->deq_seg = td->last_trb_seg; inc_deq(xhci, ep_ring); @@ -2483,7 +2477,6 @@ static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, /* Update ring dequeue pointer */ ep->ring->dequeue = td->last_trb; ep->ring->deq_seg = td->last_trb_seg; - ep->ring->num_trbs_free += td->num_trbs - 1; inc_deq(xhci, ep->ring); return xhci_td_cleanup(xhci, td, ep->ring, status); @@ -3227,7 +3220,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 (ep_ring->num_trbs_free <= num_trbs) { + } else if (xhci_num_trbs_free(xhci, ep_ring) <= num_trbs) { xhci_err(xhci, "Do not support expand command ring\n"); return -ENOMEM; } @@ -4205,7 +4198,6 @@ cleanup: ep_ring->enqueue = urb_priv->td[0].first_trb; ep_ring->enq_seg = urb_priv->td[0].start_seg; ep_ring->cycle_state = start_cycle; - ep_ring->num_trbs_free = ep_ring->num_trbs_free_temp; usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb); return ret; } @@ -4287,7 +4279,6 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, } skip_start_over: - ep_ring->num_trbs_free_temp = ep_ring->num_trbs_free; return xhci_queue_isoc_tx(xhci, mem_flags, urb, slot_id, ep_index); } diff --git a/drivers/usb/host/xhci-trace.h b/drivers/usb/host/xhci-trace.h index 7555c4ea7c4b..d6b32f2ad90e 100644 --- a/drivers/usb/host/xhci-trace.h +++ b/drivers/usb/host/xhci-trace.h @@ -458,7 +458,6 @@ DECLARE_EVENT_CLASS(xhci_log_ring, __field(unsigned int, num_segs) __field(unsigned int, stream_id) __field(unsigned int, cycle_state) - __field(unsigned int, num_trbs_free) __field(unsigned int, bounce_buf_len) ), TP_fast_assign( @@ -469,18 +468,16 @@ DECLARE_EVENT_CLASS(xhci_log_ring, __entry->enq_seg = ring->enq_seg->dma; __entry->deq_seg = ring->deq_seg->dma; __entry->cycle_state = ring->cycle_state; - __entry->num_trbs_free = ring->num_trbs_free; __entry->bounce_buf_len = ring->bounce_buf_len; __entry->enq = xhci_trb_virt_to_dma(ring->enq_seg, ring->enqueue); __entry->deq = xhci_trb_virt_to_dma(ring->deq_seg, ring->dequeue); ), - TP_printk("%s %p: enq %pad(%pad) deq %pad(%pad) segs %d stream %d free_trbs %d bounce %d cycle %d", + TP_printk("%s %p: enq %pad(%pad) deq %pad(%pad) segs %d stream %d bounce %d cycle %d", xhci_ring_type_string(__entry->type), __entry->ring, &__entry->enq, &__entry->enq_seg, &__entry->deq, &__entry->deq_seg, __entry->num_segs, __entry->stream_id, - __entry->num_trbs_free, __entry->bounce_buf_len, __entry->cycle_state ) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 56e318c384ff..456e1c8ca005 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1633,8 +1633,7 @@ struct xhci_ring { u32 cycle_state; unsigned int stream_id; unsigned int num_segs; - unsigned int num_trbs_free; - unsigned int num_trbs_free_temp; + unsigned int num_trbs_free; /* used only by xhci DbC */ unsigned int bounce_buf_len; enum xhci_ring_type type; bool last_td_was_short; -- cgit v1.2.3 From f927728186f0de1167262d6a632f9f7e96433d1a Mon Sep 17 00:00:00 2001 From: Weitao Wang Date: Fri, 2 Jun 2023 17:40:06 +0300 Subject: xhci: Fix resume issue of some ZHAOXIN hosts On ZHAOXIN ZX-100 project, xHCI can't work normally after resume from system Sx state. To fix this issue, when resume from system Sx state, reinitialize xHCI instead of restore. So, Add XHCI_RESET_ON_RESUME quirk for ZX-100 to fix issue of resuming from system Sx state. Cc: stable@vger.kernel.org Signed-off-by: Weitao Wang Signed-off-by: Mathias Nyman Message-ID: <20230602144009.1225632-9-mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 611703f863e0..2103c6c0a967 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -521,6 +521,11 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) pdev->device == PCI_DEVICE_ID_AMD_PROMONTORYA_4)) xhci->quirks |= XHCI_NO_SOFT_RETRY; + if (pdev->vendor == PCI_VENDOR_ID_ZHAOXIN) { + if (pdev->device == 0x9202) + xhci->quirks |= XHCI_RESET_ON_RESUME; + } + /* xHC spec requires PCI devices to support D3hot and D3cold */ if (xhci->hci_version >= 0x120) xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW; -- cgit v1.2.3 From 2a865a652299f5666f3b785cbe758c5f57453036 Mon Sep 17 00:00:00 2001 From: Weitao Wang Date: Fri, 2 Jun 2023 17:40:07 +0300 Subject: xhci: Fix TRB prefetch issue of ZHAOXIN hosts On some ZHAOXIN hosts, xHCI will prefetch TRB for performance improvement. However this TRB prefetch mechanism may cross page boundary, which may access memory not allocated by xHCI driver. In order to fix this issue, two pages was allocated for a segment and only the first page will be used. And add a quirk XHCI_ZHAOXIN_TRB_FETCH for this issue. Cc: stable@vger.kernel.org Signed-off-by: Weitao Wang Signed-off-by: Mathias Nyman Message-ID: <20230602144009.1225632-10-mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 8 ++++++-- drivers/usb/host/xhci-pci.c | 7 ++++++- drivers/usb/host/xhci.h | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index a8e9a4bb1537..c4170421bc9c 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2345,8 +2345,12 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) * and our use of dma addresses in the trb_address_map radix tree needs * TRB_SEGMENT_SIZE alignment, so we pick the greater alignment need. */ - xhci->segment_pool = dma_pool_create("xHCI ring segments", dev, - TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE, xhci->page_size); + if (xhci->quirks & XHCI_ZHAOXIN_TRB_FETCH) + xhci->segment_pool = dma_pool_create("xHCI ring segments", dev, + TRB_SEGMENT_SIZE * 2, TRB_SEGMENT_SIZE * 2, xhci->page_size * 2); + else + xhci->segment_pool = dma_pool_create("xHCI ring segments", dev, + TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE, xhci->page_size); /* See Table 46 and Note on Figure 55 */ xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev, diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 2103c6c0a967..3aad946bab68 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -522,8 +522,13 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_NO_SOFT_RETRY; if (pdev->vendor == PCI_VENDOR_ID_ZHAOXIN) { - if (pdev->device == 0x9202) + if (pdev->device == 0x9202) { xhci->quirks |= XHCI_RESET_ON_RESUME; + xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH; + } + + if (pdev->device == 0x9203) + xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH; } /* xHC spec requires PCI devices to support D3hot and D3cold */ diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 456e1c8ca005..5a495126c8ba 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1904,6 +1904,7 @@ struct xhci_hcd { #define XHCI_EP_CTX_BROKEN_DCS BIT_ULL(42) #define XHCI_SUSPEND_RESUME_CLKS BIT_ULL(43) #define XHCI_RESET_TO_DEFAULT BIT_ULL(44) +#define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(45) unsigned int num_active_eps; unsigned int limit_active_eps; -- cgit v1.2.3 From d9b0328d0b8b8298dfdc97cd8e0e2371d4bcc97b Mon Sep 17 00:00:00 2001 From: Weitao Wang Date: Fri, 2 Jun 2023 17:40:08 +0300 Subject: xhci: Show ZHAOXIN xHCI root hub speed correctly Some ZHAOXIN xHCI controllers follow usb3.1 spec, but only support gen1 speed 5Gbps. While in Linux kernel, if xHCI suspport usb3.1, root hub speed will show on 10Gbps. To fix this issue of ZHAOXIN xHCI platforms, read usb speed ID supported by xHCI to determine root hub speed. And add a quirk XHCI_ZHAOXIN_HOST for this issue. [fix warning about uninitialized symbol -Mathias] Suggested-by: Mathias Nyman Cc: stable@vger.kernel.org Signed-off-by: Weitao Wang Signed-off-by: Mathias Nyman Message-ID: <20230602144009.1225632-11-mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-mem.c | 31 ++++++++++++++++++++++++------- drivers/usb/host/xhci-pci.c | 2 ++ drivers/usb/host/xhci.h | 1 + 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index c4170421bc9c..19a402123de0 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1961,7 +1961,7 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, { u32 temp, port_offset, port_count; int i; - u8 major_revision, minor_revision; + u8 major_revision, minor_revision, tmp_minor_revision; struct xhci_hub *rhub; struct device *dev = xhci_to_hcd(xhci)->self.sysdev; struct xhci_port_cap *port_cap; @@ -1981,6 +1981,15 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, */ if (minor_revision > 0x00 && minor_revision < 0x10) minor_revision <<= 4; + /* + * Some zhaoxin's xHCI controller that follow usb3.1 spec + * but only support Gen1. + */ + if (xhci->quirks & XHCI_ZHAOXIN_HOST) { + tmp_minor_revision = minor_revision; + minor_revision = 0; + } + } else if (major_revision <= 0x02) { rhub = &xhci->usb2_rhub; } else { @@ -1989,10 +1998,6 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, /* Ignoring port protocol we can't understand. FIXME */ return; } - rhub->maj_rev = XHCI_EXT_PORT_MAJOR(temp); - - if (rhub->min_rev < minor_revision) - rhub->min_rev = minor_revision; /* Port offset and count in the third dword, see section 7.2 */ temp = readl(addr + 2); @@ -2010,8 +2015,6 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, if (xhci->num_port_caps > max_caps) return; - port_cap->maj_rev = major_revision; - port_cap->min_rev = minor_revision; port_cap->psi_count = XHCI_EXT_PORT_PSIC(temp); if (port_cap->psi_count) { @@ -2032,6 +2035,11 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, XHCI_EXT_PORT_PSIV(port_cap->psi[i - 1]))) port_cap->psi_uid_count++; + if (xhci->quirks & XHCI_ZHAOXIN_HOST && + major_revision == 0x03 && + XHCI_EXT_PORT_PSIV(port_cap->psi[i]) >= 5) + minor_revision = tmp_minor_revision; + xhci_dbg(xhci, "PSIV:%d PSIE:%d PLT:%d PFD:%d LP:%d PSIM:%d\n", XHCI_EXT_PORT_PSIV(port_cap->psi[i]), XHCI_EXT_PORT_PSIE(port_cap->psi[i]), @@ -2041,6 +2049,15 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, XHCI_EXT_PORT_PSIM(port_cap->psi[i])); } } + + rhub->maj_rev = major_revision; + + if (rhub->min_rev < minor_revision) + rhub->min_rev = minor_revision; + + port_cap->maj_rev = major_revision; + port_cap->min_rev = minor_revision; + /* cache usb2 port capabilities */ if (major_revision < 0x03 && xhci->num_ext_caps < max_caps) xhci->ext_caps[xhci->num_ext_caps++] = temp; diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 3aad946bab68..88c16d91fb69 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -522,6 +522,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) xhci->quirks |= XHCI_NO_SOFT_RETRY; if (pdev->vendor == PCI_VENDOR_ID_ZHAOXIN) { + xhci->quirks |= XHCI_ZHAOXIN_HOST; + if (pdev->device == 0x9202) { xhci->quirks |= XHCI_RESET_ON_RESUME; xhci->quirks |= XHCI_ZHAOXIN_TRB_FETCH; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 5a495126c8ba..7e282b4522c0 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1905,6 +1905,7 @@ struct xhci_hcd { #define XHCI_SUSPEND_RESUME_CLKS BIT_ULL(43) #define XHCI_RESET_TO_DEFAULT BIT_ULL(44) #define XHCI_ZHAOXIN_TRB_FETCH BIT_ULL(45) +#define XHCI_ZHAOXIN_HOST BIT_ULL(46) unsigned int num_active_eps; unsigned int limit_active_eps; -- cgit v1.2.3 From d5e234ff08a45a7a08a52173ed793b3c125ab88d Mon Sep 17 00:00:00 2001 From: Weitao Wang Date: Fri, 2 Jun 2023 17:40:09 +0300 Subject: xhci: Add ZHAOXIN xHCI host U1/U2 feature support Add U1/U2 feature support of xHCI for ZHAOXIN. Since both INTEL and ZHAOXIN need to check the tier where the device is located to determine whether to enabled U1/U2, remove the previous INTEL U1/U2 tier policy and add common policy in xhci_check_tier_policy. If vendor has specific U1/U2 enable policy,quirks can be add to declare. Suggested-by: Mathias Nyman Signed-off-by: Weitao Wang Signed-off-by: Mathias Nyman Message-ID: <20230602144009.1225632-12-mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-pci.c | 1 + drivers/usb/host/xhci.c | 43 ++++++++++++++++++------------------------- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 88c16d91fb69..c6742bae41c0 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -523,6 +523,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci) if (pdev->vendor == PCI_VENDOR_ID_ZHAOXIN) { xhci->quirks |= XHCI_ZHAOXIN_HOST; + xhci->quirks |= XHCI_LPM_SUPPORT; if (pdev->device == 0x9202) { xhci->quirks |= XHCI_RESET_ON_RESUME; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 176969bf2d5c..5b73a7d281ed 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4605,7 +4605,7 @@ static u16 xhci_calculate_u1_timeout(struct xhci_hcd *xhci, } } - if (xhci->quirks & XHCI_INTEL_HOST) + if (xhci->quirks & (XHCI_INTEL_HOST | XHCI_ZHAOXIN_HOST)) timeout_ns = xhci_calculate_intel_u1_timeout(udev, desc); else timeout_ns = udev->u1_params.sel; @@ -4669,7 +4669,7 @@ static u16 xhci_calculate_u2_timeout(struct xhci_hcd *xhci, } } - if (xhci->quirks & XHCI_INTEL_HOST) + if (xhci->quirks & (XHCI_INTEL_HOST | XHCI_ZHAOXIN_HOST)) timeout_ns = xhci_calculate_intel_u2_timeout(udev, desc); else timeout_ns = udev->u2_params.sel; @@ -4741,37 +4741,30 @@ static int xhci_update_timeout_for_interface(struct xhci_hcd *xhci, return 0; } -static int xhci_check_intel_tier_policy(struct usb_device *udev, +static int xhci_check_tier_policy(struct xhci_hcd *xhci, + struct usb_device *udev, enum usb3_link_state state) { - struct usb_device *parent; - unsigned int num_hubs; + struct usb_device *parent = udev->parent; + int tier = 1; /* roothub is tier1 */ - /* Don't enable U1 if the device is on a 2nd tier hub or lower. */ - for (parent = udev->parent, num_hubs = 0; parent->parent; - parent = parent->parent) - num_hubs++; + while (parent) { + parent = parent->parent; + tier++; + } - if (num_hubs < 2) - return 0; + if (xhci->quirks & XHCI_INTEL_HOST && tier > 3) + goto fail; + if (xhci->quirks & XHCI_ZHAOXIN_HOST && tier > 2) + goto fail; - dev_dbg(&udev->dev, "Disabling U1/U2 link state for device" - " below second-tier hub.\n"); - dev_dbg(&udev->dev, "Plug device into first-tier hub " - "to decrease power consumption.\n"); + return 0; +fail: + dev_dbg(&udev->dev, "Tier policy prevents U1/U2 LPM states for devices at tier %d\n", + tier); return -E2BIG; } -static int xhci_check_tier_policy(struct xhci_hcd *xhci, - struct usb_device *udev, - enum usb3_link_state state) -{ - if (xhci->quirks & XHCI_INTEL_HOST) - return xhci_check_intel_tier_policy(udev, state); - else - return 0; -} - /* Returns the U1 or U2 timeout that should be enabled. * If the tier check or timeout setting functions return with a non-zero exit * code, that means the timeout value has been finalized and we shouldn't look -- cgit v1.2.3 From 0a453dc9f260281e3a063e07b526a7e494e496fe Mon Sep 17 00:00:00 2001 From: Rajat Khandelwal Date: Wed, 31 May 2023 16:20:50 +0530 Subject: usb: typec: intel_pmc_mux: Expose IOM port status to debugfs IOM status has a crucial role during debugging to check the current state of the type-C port. There are ways to fetch the status, but all those require the IOM port status offset, which could change with platform. Make a debugfs directory for intel_pmc_mux and expose the status under it per port basis. Signed-off-by: Rajat Khandelwal Reviewed-by: Heikki Krogerus Message-ID: <20230531105050.638250-1-rajat.khandelwal@linux.intel.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/intel_pmc_mux.c | 52 ++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c index e049eadb591e..5e8edf3881c0 100644 --- a/drivers/usb/typec/mux/intel_pmc_mux.c +++ b/drivers/usb/typec/mux/intel_pmc_mux.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include @@ -143,8 +145,12 @@ struct pmc_usb { struct acpi_device *iom_adev; void __iomem *iom_base; u32 iom_port_status_offset; + + struct dentry *dentry; }; +static struct dentry *pmc_mux_debugfs_root; + static void update_port_status(struct pmc_usb_port *port) { u8 port_num; @@ -639,6 +645,29 @@ static int pmc_usb_probe_iom(struct pmc_usb *pmc) return 0; } +static int port_iom_status_show(struct seq_file *s, void *unused) +{ + struct pmc_usb_port *port = s->private; + + update_port_status(port); + seq_printf(s, "0x%08x\n", port->iom_status); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(port_iom_status); + +static void pmc_mux_port_debugfs_init(struct pmc_usb_port *port) +{ + struct dentry *debugfs_dir; + char name[6]; + + snprintf(name, sizeof(name), "port%d", port->usb3_port - 1); + + debugfs_dir = debugfs_create_dir(name, port->pmc->dentry); + debugfs_create_file("iom_status", 0400, debugfs_dir, port, + &port_iom_status_fops); +} + static int pmc_usb_probe(struct platform_device *pdev) { struct fwnode_handle *fwnode = NULL; @@ -674,6 +703,8 @@ static int pmc_usb_probe(struct platform_device *pdev) if (ret) return ret; + pmc->dentry = debugfs_create_dir(dev_name(pmc->dev), pmc_mux_debugfs_root); + /* * For every physical USB connector (USB2 and USB3 combo) there is a * child ACPI device node under the PMC mux ACPI device object. @@ -688,6 +719,8 @@ static int pmc_usb_probe(struct platform_device *pdev) fwnode_handle_put(fwnode); goto err_remove_ports; } + + pmc_mux_port_debugfs_init(&pmc->port[i]); } platform_set_drvdata(pdev, pmc); @@ -703,6 +736,8 @@ err_remove_ports: acpi_dev_put(pmc->iom_adev); + debugfs_remove(pmc->dentry); + return ret; } @@ -718,6 +753,8 @@ static void pmc_usb_remove(struct platform_device *pdev) } acpi_dev_put(pmc->iom_adev); + + debugfs_remove(pmc->dentry); } static const struct acpi_device_id pmc_usb_acpi_ids[] = { @@ -735,7 +772,20 @@ static struct platform_driver pmc_usb_driver = { .remove_new = pmc_usb_remove, }; -module_platform_driver(pmc_usb_driver); +static int __init pmc_usb_init(void) +{ + pmc_mux_debugfs_root = debugfs_create_dir("intel_pmc_mux", usb_debug_root); + + return platform_driver_register(&pmc_usb_driver); +} +module_init(pmc_usb_init); + +static void __exit pmc_usb_exit(void) +{ + platform_driver_unregister(&pmc_usb_driver); + debugfs_remove(pmc_mux_debugfs_root); +} +module_exit(pmc_usb_exit); MODULE_AUTHOR("Heikki Krogerus "); MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From ffd603f214237e250271162a5b325c6199a65382 Mon Sep 17 00:00:00 2001 From: Kuen-Han Tsai Date: Fri, 2 Jun 2023 15:00:06 +0800 Subject: usb: gadget: u_serial: Add null pointer check in gs_start_io If gs_close has cleared port->port.tty and gs_start_io is called afterwards, then the function tty_wakeup will attempt to access the value of the pointer port->port.tty which will cause a null pointer dereference error. To avoid this, add a null pointer check to gs_start_io before attempting to access the value of the pointer port->port.tty. Signed-off-by: Kuen-Han Tsai Message-ID: <20230602070009.1353946-1-khtsai@google.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/u_serial.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 97f07757d19e..1115396b46a0 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -539,16 +539,20 @@ static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head, static int gs_start_io(struct gs_port *port) { struct list_head *head = &port->read_pool; - struct usb_ep *ep = port->port_usb->out; + struct usb_ep *ep; int status; unsigned started; + if (!port->port_usb || !port->port.tty) + return -EIO; + /* Allocate RX and TX I/O buffers. We can't easily do this much * earlier (with GFP_KERNEL) because the requests are coupled to * endpoints, as are the packet sizes we'll be using. Different * configurations may use different endpoints with a given port; * and high speed vs full speed changes packet sizes too. */ + ep = port->port_usb->out; status = gs_alloc_requests(ep, head, gs_read_complete, &port->read_allocated); if (status) -- cgit v1.2.3 From f817f271dad345833929a6180a3a637f9bcc1a76 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 4 Jun 2023 13:11:18 +0200 Subject: usb: usb251xb: Use of_property_read_u16() Use of_property_read_u16() instead of of_property_read_u16_array() when only 1 element is read. This slightly simplifies the code. Signed-off-by: Christophe JAILLET Message-ID: <97478908a814d4fa694e0ca44212c3776cf3e6e9.1685877052.git.christophe.jaillet@wanadoo.fr> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/misc/usb251xb.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/usb/misc/usb251xb.c b/drivers/usb/misc/usb251xb.c index 1f3329ef5c7a..e4edb486b69e 100644 --- a/drivers/usb/misc/usb251xb.c +++ b/drivers/usb/misc/usb251xb.c @@ -416,14 +416,13 @@ static int usb251xb_get_ofdata(struct usb251xb *hub, return dev_err_probe(dev, PTR_ERR(hub->gpio_reset), "unable to request GPIO reset pin\n"); - if (of_property_read_u16_array(np, "vendor-id", &hub->vendor_id, 1)) + if (of_property_read_u16(np, "vendor-id", &hub->vendor_id)) hub->vendor_id = USB251XB_DEF_VENDOR_ID; - if (of_property_read_u16_array(np, "product-id", - &hub->product_id, 1)) + if (of_property_read_u16(np, "product-id", &hub->product_id)) hub->product_id = data->product_id; - if (of_property_read_u16_array(np, "device-id", &hub->device_id, 1)) + if (of_property_read_u16(np, "device-id", &hub->device_id)) hub->device_id = USB251XB_DEF_DEVICE_ID; hub->conf_data1 = USB251XB_DEF_CONFIG_DATA_1; @@ -532,7 +531,7 @@ static int usb251xb_get_ofdata(struct usb251xb *hub, if (!of_property_read_u32(np, "power-on-time-ms", &property_u32)) hub->power_on_time = min_t(u8, property_u32 / 2, 255); - if (of_property_read_u16_array(np, "language-id", &hub->lang_id, 1)) + if (of_property_read_u16(np, "language-id", &hub->lang_id)) hub->lang_id = USB251XB_DEF_LANGUAGE_ID; if (of_property_read_u8(np, "boost-up", &hub->boost_up)) -- cgit v1.2.3 From 152669f844d11513fc438bfee01d3f1bb2b2b565 Mon Sep 17 00:00:00 2001 From: Piyush Mehta Date: Wed, 7 Jun 2023 12:59:59 +0530 Subject: usb: gadget: udc: udc-xilinx: Add identifier to read_fn function arg Add an identifier in the read_fn function declaration because based on commit ca0d8929e75a ("checkpatch: add warning for unnamed function definition arguments") it is the preferred coding style even C standard allows both formats. Signed-off-by: Piyush Mehta Message-ID: <20230607072959.2334046-1-piyush.mehta@amd.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/udc-xilinx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index d73111b5cd84..a4a7b90a97e7 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -192,7 +192,7 @@ struct xusb_udc { bool dma_enabled; struct clk *clk; - unsigned int (*read_fn)(void __iomem *); + unsigned int (*read_fn)(void __iomem *reg); void (*write_fn)(void __iomem *, u32, u32); }; -- cgit v1.2.3 From 5ae8a35459e77fd9ddb1844baa8c736fc0223847 Mon Sep 17 00:00:00 2001 From: Avichal Rakesh Date: Fri, 2 Jun 2023 15:04:55 -0700 Subject: usb: gadget: uvc: clean up comments and styling in video_pump This patch elaborates on some of the edge cases handled by video_pump around setting no_interrupt flag, and brings the code style in line with rest of the file. Link: https://lore.kernel.org/20230602151916.GH26944@pendragon.ideasonboard.com/ Signed-off-by: Avichal Rakesh Reviewed-by: Laurent Pinchart Message-ID: <20230602220455.313801-1-arakesh@google.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/uvc_video.c | 38 ++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index e81865978299..91af3b1ef0d4 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -382,13 +382,13 @@ static void uvcg_video_pump(struct work_struct *work) { struct uvc_video *video = container_of(work, struct uvc_video, pump); struct uvc_video_queue *queue = &video->queue; + /* video->max_payload_size is only set when using bulk transfer */ + bool is_bulk = video->max_payload_size; struct usb_request *req = NULL; struct uvc_buffer *buf; unsigned long flags; + bool buf_done; int ret; - bool buf_int; - /* video->max_payload_size is only set when using bulk transfer */ - bool is_bulk = video->max_payload_size; while (video->ep->enabled) { /* @@ -414,20 +414,19 @@ static void uvcg_video_pump(struct work_struct *work) if (buf != NULL) { video->encode(req, video, buf); - /* Always interrupt for the last request of a video buffer */ - buf_int = buf->state == UVC_BUF_STATE_DONE; + buf_done = buf->state == UVC_BUF_STATE_DONE; } else if (!(queue->flags & UVC_QUEUE_DISCONNECTED) && !is_bulk) { /* * No video buffer available; the queue is still connected and - * we're traferring over ISOC. Queue a 0 length request to + * we're transferring over ISOC. Queue a 0 length request to * prevent missed ISOC transfers. */ req->length = 0; - buf_int = false; + buf_done = false; } else { /* - * Either queue has been disconnected or no video buffer - * available to bulk transfer. Either way, stop processing + * Either the queue has been disconnected or no video buffer + * available for bulk transfer. Either way, stop processing * further. */ spin_unlock_irqrestore(&queue->irqlock, flags); @@ -435,11 +434,24 @@ static void uvcg_video_pump(struct work_struct *work) } /* - * With usb3 we have more requests. This will decrease the - * interrupt load to a quarter but also catches the corner - * cases, which needs to be handled. + * With USB3 handling more requests at a higher speed, we can't + * afford to generate an interrupt for every request. Decide to + * interrupt: + * + * - When no more requests are available in the free queue, as + * this may be our last chance to refill the endpoint's + * request queue. + * + * - When this is request is the last request for the video + * buffer, as we want to start sending the next video buffer + * ASAP in case it doesn't get started already in the next + * iteration of this loop. + * + * - Four times over the length of the requests queue (as + * indicated by video->uvc_num_requests), as a trade-off + * between latency and interrupt load. */ - if (list_empty(&video->req_free) || buf_int || + if (list_empty(&video->req_free) || buf_done || !(video->req_int_count % DIV_ROUND_UP(video->uvc_num_requests, 4))) { video->req_int_count = 0; -- cgit v1.2.3 From 6b394dbb6469e438c537f84899402ffdb8fcbdbd Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Wed, 7 Jun 2023 23:54:02 +0200 Subject: usb: gadget: f_mass_storage: remove unnecessary open check The fsg_lun_is_open() test can be eliminated and the code merged with the preceding conditional, because the LUN won't be open if cfg->filename wasn't set. Similarly, the error_lun label will never be reached with an open lun (non-null filp) so remove the unnecessary fsg_lun_close() call. Signed-off-by: David Disseldorp Reviewed-by: Alan Stern Message-ID: <20230607215401.22563-1-ddiss@suse.de> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_mass_storage.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 3a30feb47073..da07e45ae6df 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -2857,7 +2857,7 @@ int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg, const char **name_pfx) { struct fsg_lun *lun; - char *pathbuf, *p; + char *pathbuf = NULL, *p = "(no medium)"; int rc = -ENOMEM; if (id >= ARRAY_SIZE(common->luns)) @@ -2907,12 +2907,9 @@ int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg, rc = fsg_lun_open(lun, cfg->filename); if (rc) goto error_lun; - } - pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); - p = "(no medium)"; - if (fsg_lun_is_open(lun)) { p = "(error)"; + pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); if (pathbuf) { p = file_path(lun->filp, pathbuf, PATH_MAX); if (IS_ERR(p)) @@ -2931,7 +2928,6 @@ int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg, error_lun: if (device_is_registered(&lun->dev)) device_unregister(&lun->dev); - fsg_lun_close(lun); common->luns[id] = NULL; error_sysfs: kfree(lun); -- cgit v1.2.3 From 8fd95da2cfb5046c4bb5a3cdc9eb7963ba8b10dd Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 4 Jun 2023 17:04:37 +0200 Subject: usb: dwc3: qcom: Release the correct resources in dwc3_qcom_remove() In the probe, some resources are allocated with dwc3_qcom_of_register_core() or dwc3_qcom_acpi_register_core(). The corresponding resources are already coorectly freed in the error handling path of the probe, but not in the remove function. Fix it. Fixes: 2bc02355f8ba ("usb: dwc3: qcom: Add support for booting with ACPI") Signed-off-by: Christophe JAILLET Reviewed-by: Andrew Halaney Message-ID: Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-qcom.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 822735814050..ad90e1ce39ad 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -941,11 +941,15 @@ reset_assert: static void dwc3_qcom_remove(struct platform_device *pdev) { struct dwc3_qcom *qcom = platform_get_drvdata(pdev); + struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; int i; device_remove_software_node(&qcom->dwc3->dev); - of_platform_depopulate(dev); + if (np) + of_platform_depopulate(&pdev->dev); + else + platform_device_put(pdev); for (i = qcom->num_clocks - 1; i >= 0; i--) { clk_disable_unprepare(qcom->clks[i]); -- cgit v1.2.3 From 4a944da707123686d372ec01ea60056902fadf35 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 4 Jun 2023 16:56:34 +0200 Subject: usb: dwc3: qcom: Fix an error handling path in dwc3_qcom_probe() If dwc3_qcom_create_urs_usb_platdev() fails, some resources still need to be released, as already done in the other error handling path of the probe. Fixes: c25c210f590e ("usb: dwc3: qcom: add URS Host support for sdm845 ACPI boot") Signed-off-by: Christophe JAILLET Reviewed-by: Andrew Halaney Message-ID: Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-qcom.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index ad90e1ce39ad..9c95f1d909ba 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -856,9 +856,10 @@ static int dwc3_qcom_probe(struct platform_device *pdev) if (IS_ERR_OR_NULL(qcom->urs_usb)) { dev_err(dev, "failed to create URS USB platdev\n"); if (!qcom->urs_usb) - return -ENODEV; + ret = -ENODEV; else - return PTR_ERR(qcom->urs_usb); + ret = PTR_ERR(qcom->urs_usb); + goto clk_disable; } } } -- cgit v1.2.3 From 60d5b71933c4f1d818aff15ec8b2a8f83a3843b2 Mon Sep 17 00:00:00 2001 From: Andrew Halaney Date: Mon, 5 Jun 2023 14:36:25 -0500 Subject: usb: dwc3: qcom: use dev_err_probe() where appropriate Update to using dev_err_probe() throughout to reduce spam and log useful information in devices_deferred. Signed-off-by: Andrew Halaney Message-ID: <20230605193625.63187-1-ahalaney@redhat.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-qcom.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 9c95f1d909ba..7379cb62960c 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -167,7 +167,8 @@ static int dwc3_qcom_register_extcon(struct dwc3_qcom *qcom) qcom->edev = extcon_get_edev_by_phandle(dev, 0); if (IS_ERR(qcom->edev)) - return PTR_ERR(qcom->edev); + return dev_err_probe(dev, PTR_ERR(qcom->edev), + "Failed to get extcon\n"); qcom->vbus_nb.notifier_call = dwc3_qcom_vbus_notifier; @@ -252,16 +253,14 @@ static int dwc3_qcom_interconnect_init(struct dwc3_qcom *qcom) qcom->icc_path_ddr = of_icc_get(dev, "usb-ddr"); if (IS_ERR(qcom->icc_path_ddr)) { - dev_err(dev, "failed to get usb-ddr path: %ld\n", - PTR_ERR(qcom->icc_path_ddr)); - return PTR_ERR(qcom->icc_path_ddr); + return dev_err_probe(dev, PTR_ERR(qcom->icc_path_ddr), + "failed to get usb-ddr path\n"); } qcom->icc_path_apps = of_icc_get(dev, "apps-usb"); if (IS_ERR(qcom->icc_path_apps)) { - dev_err(dev, "failed to get apps-usb path: %ld\n", - PTR_ERR(qcom->icc_path_apps)); - ret = PTR_ERR(qcom->icc_path_apps); + ret = dev_err_probe(dev, PTR_ERR(qcom->icc_path_apps), + "failed to get apps-usb path\n"); goto put_path_ddr; } @@ -813,9 +812,8 @@ static int dwc3_qcom_probe(struct platform_device *pdev) qcom->resets = devm_reset_control_array_get_optional_exclusive(dev); if (IS_ERR(qcom->resets)) { - ret = PTR_ERR(qcom->resets); - dev_err(&pdev->dev, "failed to get resets, err=%d\n", ret); - return ret; + return dev_err_probe(&pdev->dev, PTR_ERR(qcom->resets), + "failed to get resets\n"); } ret = reset_control_assert(qcom->resets); @@ -834,7 +832,7 @@ static int dwc3_qcom_probe(struct platform_device *pdev) ret = dwc3_qcom_clk_init(qcom, of_clk_get_parent_count(np)); if (ret) { - dev_err(dev, "failed to get clocks\n"); + dev_err_probe(dev, ret, "failed to get clocks\n"); goto reset_assert; } -- cgit v1.2.3 From 52ff079dede27c67f106d6f05eeba3650c000439 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Tue, 30 May 2023 18:40:06 +0800 Subject: dt-bindings: usb: ci-hdrc-usb2: add fsl,imx8ulp-usb compatible The imx8ulp and imx8dxl are compatible. This will add fsl,imx8ulp-usb to the compatible property. Signed-off-by: Xu Yang Acked-by: Krzysztof Kozlowski Message-ID: <20230530104007.1294702-1-xu.yang_2@nxp.com> Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml index b26d26c2b023..782402800d4a 100644 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml +++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml @@ -45,7 +45,9 @@ properties: - fsl,vf610-usb - const: fsl,imx27-usb - items: - - const: fsl,imx8dxl-usb + - enum: + - fsl,imx8dxl-usb + - fsl,imx8ulp-usb - const: fsl,imx7ulp-usb - const: fsl,imx6ul-usb - items: -- cgit v1.2.3 From 9a070e8e208995a9d638b538ed7abf28bd6ea6f0 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Tue, 30 May 2023 18:40:07 +0800 Subject: usb: chipidea: imx: don't request QoS for imx8ulp Use dedicated imx8ulp usb compatible to remove QoS request since imx8ulp has no such limitation of imx7ulp: DMA will not work if system enters idle. Signed-off-by: Xu Yang Signed-off-by: Li Jun Acked-by: Peter Chen Message-ID: <20230530104007.1294702-2-xu.yang_2@nxp.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/ci_hdrc_imx.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 9f0f4ec701c5..336ef6dd8e7d 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -70,6 +70,10 @@ static const struct ci_hdrc_imx_platform_flag imx7ulp_usb_data = { CI_HDRC_PMQOS, }; +static const struct ci_hdrc_imx_platform_flag imx8ulp_usb_data = { + .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, +}; + static const struct of_device_id ci_hdrc_imx_dt_ids[] = { { .compatible = "fsl,imx23-usb", .data = &imx23_usb_data}, { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, @@ -80,6 +84,7 @@ static const struct of_device_id ci_hdrc_imx_dt_ids[] = { { .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data}, { .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data}, { .compatible = "fsl,imx7ulp-usb", .data = &imx7ulp_usb_data}, + { .compatible = "fsl,imx8ulp-usb", .data = &imx8ulp_usb_data}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); -- cgit v1.2.3 From edd60d24bd858cef165274e4cd6cab43bdc58d15 Mon Sep 17 00:00:00 2001 From: Prashanth K Date: Wed, 31 May 2023 20:11:14 +0530 Subject: usb: common: usb-conn-gpio: Set last role to unknown before initial detection Currently if we bootup a device without cable connected, then usb-conn-gpio won't call set_role() since last_role is same as current role. This happens because during probe last_role gets initialised to zero. To avoid this, added a new constant in enum usb_role, last_role is set to USB_ROLE_UNKNOWN before performing initial detection. While at it, also handle default case for the usb_role switch in cdns3, intel-xhci-usb-role-switch & musb/jz4740 to avoid build warnings. Fixes: 4602f3bff266 ("usb: common: add USB GPIO based connection detection driver") Signed-off-by: Prashanth K Reviewed-by: AngeloGioacchino Del Regno Message-ID: <1685544074-17337-1-git-send-email-quic_prashk@quicinc.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/core.c | 2 ++ drivers/usb/common/usb-conn-gpio.c | 3 +++ drivers/usb/musb/jz4740.c | 2 ++ drivers/usb/roles/intel-xhci-usb-role-switch.c | 2 ++ include/linux/usb/role.h | 1 + 5 files changed, 10 insertions(+) diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index dbcdf3b24b47..69d2921f2d3b 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -252,6 +252,8 @@ static enum usb_role cdns_hw_role_state_machine(struct cdns *cdns) if (!vbus) role = USB_ROLE_NONE; break; + default: + break; } dev_dbg(cdns->dev, "role %d -> %d\n", cdns->role, role); diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c index 766005d20bae..d0e8624c7dfe 100644 --- a/drivers/usb/common/usb-conn-gpio.c +++ b/drivers/usb/common/usb-conn-gpio.c @@ -257,6 +257,9 @@ static int usb_conn_probe(struct platform_device *pdev) platform_set_drvdata(pdev, info); device_set_wakeup_capable(&pdev->dev, true); + /* Set last role to unknown before performing the initial detection */ + info->last_role = USB_ROLE_UNKNOWN; + /* Perform initial detection */ usb_conn_queue_dwork(info, 0); diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index 5aabdd7e2511..6d880c4cce36 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -95,6 +95,8 @@ static int jz4740_musb_role_switch_set(struct usb_role_switch *sw, case USB_ROLE_HOST: atomic_notifier_call_chain(&phy->notifier, USB_EVENT_ID, phy); break; + default: + break; } return 0; diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c index e5c6c413a075..8e2997d65f11 100644 --- a/drivers/usb/roles/intel-xhci-usb-role-switch.c +++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c @@ -97,6 +97,8 @@ static int intel_xhci_usb_set_role(struct usb_role_switch *sw, val |= SW_VBUS_VALID; drd_config = DRD_CONFIG_STATIC_DEVICE; break; + default: + break; } val |= SW_IDPIN_EN; if (data->enable_sw_switch) { diff --git a/include/linux/usb/role.h b/include/linux/usb/role.h index b5deafd91f67..65e790a28913 100644 --- a/include/linux/usb/role.h +++ b/include/linux/usb/role.h @@ -11,6 +11,7 @@ enum usb_role { USB_ROLE_NONE, USB_ROLE_HOST, USB_ROLE_DEVICE, + USB_ROLE_UNKNOWN, }; typedef int (*usb_role_switch_set_t)(struct usb_role_switch *sw, -- cgit v1.2.3 From 83cb2604f641cecadc275ca18adbba4bf262320f Mon Sep 17 00:00:00 2001 From: Roy Luo Date: Thu, 8 Jun 2023 01:59:12 +0000 Subject: usb: core: add sysfs entry for usb device state Expose usb device state to userland as the information is useful in detecting non-compliant setups and diagnosing enumeration failures. For example: - End-to-end signal integrity issues: the device would fail port reset repeatedly and thus be stuck in POWERED state. - Charge-only cables (missing D+/D- lines): the device would never enter POWERED state as the HC would not see any pullup. What's the status quo? We do have error logs such as "Cannot enable. Maybe the USB cable is bad?" to flag potential setup issues, but there's no good way to expose them to userspace. Why add a sysfs entry in struct usb_port instead of struct usb_device? The struct usb_device is not device_add() to the system until it's in ADDRESS state hence we would miss the first two states. The struct usb_port is a better place to keep the information because its life cycle is longer than the struct usb_device that is attached to the port. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-lkp/202306042228.e532af6e-oliver.sang@intel.com Reviewed-by: Alan Stern Signed-off-by: Roy Luo Message-ID: <20230608015913.1679984-1-royluo@google.com> Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-bus-usb | 10 ++++++++++ drivers/usb/core/hub.c | 15 +++++++++++++++ drivers/usb/core/hub.h | 4 ++++ drivers/usb/core/port.c | 32 ++++++++++++++++++++++++++++---- 4 files changed, 57 insertions(+), 4 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index cb172db41b34..be663258b9b7 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -292,6 +292,16 @@ Description: which is marked with early_stop has failed to initialize, it will ignore all future connections until this attribute is clear. +What: /sys/bus/usb/devices/...//port/state +Date: June 2023 +Contact: Roy Luo +Description: + Indicates current state of the USB device attached to the port. + Valid states are: 'not-attached', 'attached', 'powered', + 'reconnecting', 'unauthenticated', 'default', 'addressed', + 'configured', and 'suspended'. This file supports poll() to + monitor the state change from user space. + What: /sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout Date: May 2013 Contact: Mathias Nyman diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 97a0f8faea6e..a739403a9e45 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2018,6 +2018,19 @@ bool usb_device_is_owned(struct usb_device *udev) return !!hub->ports[udev->portnum - 1]->port_owner; } +static void update_port_device_state(struct usb_device *udev) +{ + struct usb_hub *hub; + struct usb_port *port_dev; + + if (udev->parent) { + hub = usb_hub_to_struct_hub(udev->parent); + port_dev = hub->ports[udev->portnum - 1]; + WRITE_ONCE(port_dev->state, udev->state); + sysfs_notify_dirent(port_dev->state_kn); + } +} + static void recursively_mark_NOTATTACHED(struct usb_device *udev) { struct usb_hub *hub = usb_hub_to_struct_hub(udev); @@ -2030,6 +2043,7 @@ static void recursively_mark_NOTATTACHED(struct usb_device *udev) if (udev->state == USB_STATE_SUSPENDED) udev->active_duration -= jiffies; udev->state = USB_STATE_NOTATTACHED; + update_port_device_state(udev); } /** @@ -2086,6 +2100,7 @@ void usb_set_device_state(struct usb_device *udev, udev->state != USB_STATE_SUSPENDED) udev->active_duration += jiffies; udev->state = new_state; + update_port_device_state(udev); } else recursively_mark_NOTATTACHED(udev); spin_unlock_irqrestore(&device_state_lock, flags); diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index e23833562e4f..37897afd1b64 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -84,6 +84,8 @@ struct usb_hub { * @peer: related usb2 and usb3 ports (share the same connector) * @req: default pm qos request for hubs without port power control * @connect_type: port's connect type + * @state: device state of the usb device attached to the port + * @state_kn: kernfs_node of the sysfs attribute that accesses @state * @location: opaque representation of platform connector location * @status_lock: synchronize port_event() vs usb_port_{suspend|resume} * @portnum: port index num based one @@ -100,6 +102,8 @@ struct usb_port { struct usb_port *peer; struct dev_pm_qos_request *req; enum usb_port_connect_type connect_type; + enum usb_device_state state; + struct kernfs_node *state_kn; usb_port_location_t location; struct mutex status_lock; u32 over_current_count; diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index 06a8f1f84f6f..77be0dc28da9 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -160,6 +160,16 @@ static ssize_t connect_type_show(struct device *dev, } static DEVICE_ATTR_RO(connect_type); +static ssize_t state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_port *port_dev = to_usb_port(dev); + enum usb_device_state state = READ_ONCE(port_dev->state); + + return sysfs_emit(buf, "%s\n", usb_state_string(state)); +} +static DEVICE_ATTR_RO(state); + static ssize_t over_current_count_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -259,6 +269,7 @@ static DEVICE_ATTR_RW(usb3_lpm_permit); static struct attribute *port_dev_attrs[] = { &dev_attr_connect_type.attr, + &dev_attr_state.attr, &dev_attr_location.attr, &dev_attr_quirks.attr, &dev_attr_over_current_count.attr, @@ -705,19 +716,24 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1) return retval; } + port_dev->state_kn = sysfs_get_dirent(port_dev->dev.kobj.sd, "state"); + if (!port_dev->state_kn) { + dev_err(&port_dev->dev, "failed to sysfs_get_dirent 'state'\n"); + retval = -ENODEV; + goto err_unregister; + } + /* Set default policy of port-poweroff disabled. */ retval = dev_pm_qos_add_request(&port_dev->dev, port_dev->req, DEV_PM_QOS_FLAGS, PM_QOS_FLAG_NO_POWER_OFF); if (retval < 0) { - device_unregister(&port_dev->dev); - return retval; + goto err_put_kn; } retval = component_add(&port_dev->dev, &connector_ops); if (retval) { dev_warn(&port_dev->dev, "failed to add component\n"); - device_unregister(&port_dev->dev); - return retval; + goto err_put_kn; } find_and_link_peer(hub, port1); @@ -754,6 +770,13 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1) port_dev->req = NULL; } return 0; + +err_put_kn: + sysfs_put(port_dev->state_kn); +err_unregister: + device_unregister(&port_dev->dev); + + return retval; } void usb_hub_remove_port_device(struct usb_hub *hub, int port1) @@ -765,5 +788,6 @@ void usb_hub_remove_port_device(struct usb_hub *hub, int port1) if (peer) unlink_peers(port_dev, peer); component_del(&port_dev->dev, &connector_ops); + sysfs_put(port_dev->state_kn); device_unregister(&port_dev->dev); } -- cgit v1.2.3 From a053d9dc45acb9534ab4ff1e5794c73011ae5d4d Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 12 Jun 2023 06:45:18 -0400 Subject: usb: update the ctime as well when updating mtime after an ioctl In general, POSIX requires that when the mtime is updated that the ctime be updated as well. Add the missing timestamp updates to the usb ioctls. Signed-off-by: Jeff Layton Message-ID: <20230612104524.17058-3-jlayton@kernel.org> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/devio.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index cbad2af5fd88..1a16a8bdea60 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -2642,21 +2642,21 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, snoop(&dev->dev, "%s: CONTROL\n", __func__); ret = proc_control(ps, p); if (ret >= 0) - inode->i_mtime = current_time(inode); + inode->i_mtime = inode->i_ctime = current_time(inode); break; case USBDEVFS_BULK: snoop(&dev->dev, "%s: BULK\n", __func__); ret = proc_bulk(ps, p); if (ret >= 0) - inode->i_mtime = current_time(inode); + inode->i_mtime = inode->i_ctime = current_time(inode); break; case USBDEVFS_RESETEP: snoop(&dev->dev, "%s: RESETEP\n", __func__); ret = proc_resetep(ps, p); if (ret >= 0) - inode->i_mtime = current_time(inode); + inode->i_mtime = inode->i_ctime = current_time(inode); break; case USBDEVFS_RESET: @@ -2668,7 +2668,7 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, snoop(&dev->dev, "%s: CLEAR_HALT\n", __func__); ret = proc_clearhalt(ps, p); if (ret >= 0) - inode->i_mtime = current_time(inode); + inode->i_mtime = inode->i_ctime = current_time(inode); break; case USBDEVFS_GETDRIVER: @@ -2695,7 +2695,7 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, snoop(&dev->dev, "%s: SUBMITURB\n", __func__); ret = proc_submiturb(ps, p); if (ret >= 0) - inode->i_mtime = current_time(inode); + inode->i_mtime = inode->i_ctime = current_time(inode); break; #ifdef CONFIG_COMPAT @@ -2703,14 +2703,14 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, snoop(&dev->dev, "%s: CONTROL32\n", __func__); ret = proc_control_compat(ps, p); if (ret >= 0) - inode->i_mtime = current_time(inode); + inode->i_mtime = inode->i_ctime = current_time(inode); break; case USBDEVFS_BULK32: snoop(&dev->dev, "%s: BULK32\n", __func__); ret = proc_bulk_compat(ps, p); if (ret >= 0) - inode->i_mtime = current_time(inode); + inode->i_mtime = inode->i_ctime = current_time(inode); break; case USBDEVFS_DISCSIGNAL32: @@ -2722,7 +2722,7 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd, snoop(&dev->dev, "%s: SUBMITURB32\n", __func__); ret = proc_submiturb_compat(ps, p); if (ret >= 0) - inode->i_mtime = current_time(inode); + inode->i_mtime = inode->i_ctime = current_time(inode); break; case USBDEVFS_IOCTL32: -- cgit v1.2.3 From 01052b91c9808e3c3b068ae2721cb728ec9aa4c0 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 10 Jun 2023 15:32:52 +0200 Subject: usb: dwc3-meson-g12a: Fix an error handling path in dwc3_meson_g12a_probe() If dwc3_meson_g12a_otg_init() fails, resources allocated by the previous of_platform_populate() call should be released, as already done in the error handling path. Fixes: 1e355f21d3fb ("usb: dwc3: Add Amlogic A1 DWC3 glue") Signed-off-by: Christophe JAILLET Reviewed-by: Martin Blumenstingl Reviewed-by: Neil Armstrong Message-ID: <9d28466de1808ccc756b4cc25fc72c482d133d13.1686403934.git.christophe.jaillet@wanadoo.fr> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/dwc3-meson-g12a.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c index 365aec00d302..e99c7489dba0 100644 --- a/drivers/usb/dwc3/dwc3-meson-g12a.c +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c @@ -796,7 +796,7 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) ret = dwc3_meson_g12a_otg_init(pdev, priv); if (ret) - goto err_phys_power; + goto err_plat_depopulate; pm_runtime_set_active(dev); pm_runtime_enable(dev); @@ -804,6 +804,9 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev) return 0; +err_plat_depopulate: + of_platform_depopulate(dev); + err_phys_power: for (i = 0 ; i < PHY_COUNT ; ++i) phy_power_off(priv->phys[i]); -- cgit v1.2.3 From 0ca2026eea104adc1d0d356bca35915a1ab6e3e9 Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Fri, 2 Jun 2023 06:26:41 -0400 Subject: usb: cdns2: Device side header file for CDNS2 driver Patch defines macros, registers and structures used by Device side driver. Signed-off-by: Pawel Laszczak Message-ID: <20230602102644.77470-2-pawell@cadence.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/cdns2/cdns2-gadget.h | 707 ++++++++++++++++++++++++++++ 1 file changed, 707 insertions(+) create mode 100644 drivers/usb/gadget/udc/cdns2/cdns2-gadget.h diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.h b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.h new file mode 100644 index 000000000000..71e2f62d653a --- /dev/null +++ b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.h @@ -0,0 +1,707 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * USBHS-DEV device controller driver header file + * + * Copyright (C) 2023 Cadence. + * + * Author: Pawel Laszczak + */ + +#ifndef __LINUX_CDNS2_GADGET +#define __LINUX_CDNS2_GADGET + +#include +#include + +/* + * USBHS register interface. + * This corresponds to the USBHS Device Controller Interface. + */ + +/** + * struct cdns2_ep0_regs - endpoint 0 related registers. + * @rxbc: receive (OUT) 0 endpoint byte count register. + * @txbc: transmit (IN) 0 endpoint byte count register. + * @cs: 0 endpoint control and status register. + * @reserved1: reserved. + * @fifo: 0 endpoint fifo register. + * @reserved2: reserved. + * @setupdat: SETUP data register. + * @reserved4: reserved. + * @maxpack: 0 endpoint max packet size. + */ +struct cdns2_ep0_regs { + __u8 rxbc; + __u8 txbc; + __u8 cs; + __u8 reserved1[4]; + __u8 fifo; + __le32 reserved2[94]; + __u8 setupdat[8]; + __u8 reserved4[88]; + __u8 maxpack; +} __packed __aligned(4); + +/* EP0CS - bitmasks. */ +/* Endpoint 0 stall bit for status stage. */ +#define EP0CS_STALL BIT(0) +/* HSNAK bit. */ +#define EP0CS_HSNAK BIT(1) +/* IN 0 endpoint busy bit. */ +#define EP0CS_TXBSY_MSK BIT(2) +/* OUT 0 endpoint busy bit. */ +#define EP0CS_RXBSY_MSK BIT(3) +/* Send STALL in the data stage phase. */ +#define EP0CS_DSTALL BIT(4) +/* SETUP buffer content was changed. */ +#define EP0CS_CHGSET BIT(7) + +/* EP0FIFO - bitmasks. */ +/* Direction. */ +#define EP0_FIFO_IO_TX BIT(4) +/* FIFO auto bit. */ +#define EP0_FIFO_AUTO BIT(5) +/* FIFO commit bit. */ +#define EP0_FIFO_COMMIT BIT(6) +/* FIFO access bit. */ +#define EP0_FIFO_ACCES BIT(7) + +/** + * struct cdns2_epx_base - base endpoint registers. + * @rxbc: OUT endpoint byte count register. + * @rxcon: OUT endpoint control register. + * @rxcs: OUT endpoint control and status register. + * @txbc: IN endpoint byte count register. + * @txcon: IN endpoint control register. + * @txcs: IN endpoint control and status register. + */ +struct cdns2_epx_base { + __le16 rxbc; + __u8 rxcon; + __u8 rxcs; + __le16 txbc; + __u8 txcon; + __u8 txcs; +} __packed __aligned(4); + +/* rxcon/txcon - endpoint control register bitmasks. */ +/* Endpoint buffering: 0 - single buffering ... 3 - quad buffering. */ +#define EPX_CON_BUF GENMASK(1, 0) +/* Endpoint type. */ +#define EPX_CON_TYPE GENMASK(3, 2) +/* Endpoint type: isochronous. */ +#define EPX_CON_TYPE_ISOC 0x4 +/* Endpoint type: bulk. */ +#define EPX_CON_TYPE_BULK 0x8 +/* Endpoint type: interrupt. */ +#define EPX_CON_TYPE_INT 0xC +/* Number of packets per microframe. */ +#define EPX_CON_ISOD GENMASK(5, 4) +#define EPX_CON_ISOD_SHIFT 0x4 +/* Endpoint stall bit. */ +#define EPX_CON_STALL BIT(6) +/* Endpoint enable bit.*/ +#define EPX_CON_VAL BIT(7) + +/* rxcs/txcs - endpoint control and status bitmasks. */ +/* Data sequence error for the ISO endpoint. */ +#define EPX_CS_ERR(p) ((p) & BIT(0)) + +/** + * struct cdns2_epx_regs - endpoint 1..15 related registers. + * @reserved: reserved. + * @ep: none control endpoints array. + * @reserved2: reserved. + * @endprst: endpoint reset register. + * @reserved3: reserved. + * @isoautoarm: ISO auto-arm register. + * @reserved4: reserved. + * @isodctrl: ISO control register. + * @reserved5: reserved. + * @isoautodump: ISO auto dump enable register. + * @reserved6: reserved. + * @rxmaxpack: receive (OUT) Max packet size register. + * @reserved7: reserved. + * @rxstaddr: receive (OUT) start address endpoint buffer register. + * @reserved8: reserved. + * @txstaddr: transmit (IN) start address endpoint buffer register. + * @reserved9: reserved. + * @txmaxpack: transmit (IN) Max packet size register. + */ +struct cdns2_epx_regs { + __le32 reserved[2]; + struct cdns2_epx_base ep[15]; + __u8 reserved2[290]; + __u8 endprst; + __u8 reserved3[41]; + __le16 isoautoarm; + __u8 reserved4[10]; + __le16 isodctrl; + __le16 reserved5; + __le16 isoautodump; + __le32 reserved6; + __le16 rxmaxpack[15]; + __le32 reserved7[65]; + __le32 rxstaddr[15]; + __u8 reserved8[4]; + __le32 txstaddr[15]; + __u8 reserved9[98]; + __le16 txmaxpack[15]; +} __packed __aligned(4); + +/* ENDPRST - bitmasks. */ +/* Endpoint number. */ +#define ENDPRST_EP GENMASK(3, 0) +/* IN direction bit. */ +#define ENDPRST_IO_TX BIT(4) +/* Toggle reset bit. */ +#define ENDPRST_TOGRST BIT(5) +/* FIFO reset bit. */ +#define ENDPRST_FIFORST BIT(6) +/* Toggle status and reset bit. */ +#define ENDPRST_TOGSETQ BIT(7) + +/** + * struct cdns2_interrupt_regs - USB interrupt related registers. + * @reserved: reserved. + * @usbirq: USB interrupt request register. + * @extirq: external interrupt request register. + * @rxpngirq: external interrupt request register. + * @reserved1: reserved. + * @usbien: USB interrupt enable register. + * @extien: external interrupt enable register. + * @reserved2: reserved. + * @usbivect: USB interrupt vector register. + */ +struct cdns2_interrupt_regs { + __u8 reserved[396]; + __u8 usbirq; + __u8 extirq; + __le16 rxpngirq; + __le16 reserved1[4]; + __u8 usbien; + __u8 extien; + __le16 reserved2[3]; + __u8 usbivect; +} __packed __aligned(4); + +/* EXTIRQ and EXTIEN - bitmasks. */ +/* VBUS fault fall interrupt. */ +#define EXTIRQ_VBUSFAULT_FALL BIT(0) +/* VBUS fault fall interrupt. */ +#define EXTIRQ_VBUSFAULT_RISE BIT(1) +/* Wake up interrupt bit. */ +#define EXTIRQ_WAKEUP BIT(7) + +/* USBIEN and USBIRQ - bitmasks. */ +/* SETUP data valid interrupt bit.*/ +#define USBIRQ_SUDAV BIT(0) +/* Start-of-frame interrupt bit. */ +#define USBIRQ_SOF BIT(1) +/* SETUP token interrupt bit. */ +#define USBIRQ_SUTOK BIT(2) +/* USB suspend interrupt bit. */ +#define USBIRQ_SUSPEND BIT(3) +/* USB reset interrupt bit. */ +#define USBIRQ_URESET BIT(4) +/* USB high-speed mode interrupt bit. */ +#define USBIRQ_HSPEED BIT(5) +/* Link Power Management interrupt bit. */ +#define USBIRQ_LPM BIT(7) + +#define USB_IEN_INIT (USBIRQ_SUDAV | USBIRQ_SUSPEND | USBIRQ_URESET \ + | USBIRQ_HSPEED | USBIRQ_LPM) +/** + * struct cdns2_usb_regs - USB controller registers. + * @reserved: reserved. + * @lpmctrl: LPM control register. + * @lpmclock: LPM clock register. + * @reserved2: reserved. + * @endprst: endpoint reset register. + * @usbcs: USB control and status register. + * @frmnr: USB frame counter register. + * @fnaddr: function Address register. + * @clkgate: clock gate register. + * @fifoctrl: FIFO control register. + * @speedctrl: speed Control register. + * @sleep_clkgate: sleep Clock Gate register. + * @reserved3: reserved. + * @cpuctrl: microprocessor control register. + */ +struct cdns2_usb_regs { + __u8 reserved[4]; + __u16 lpmctrl; + __u8 lpmclock; + __u8 reserved2[411]; + __u8 endprst; + __u8 usbcs; + __le16 frmnr; + __u8 fnaddr; + __u8 clkgate; + __u8 fifoctrl; + __u8 speedctrl; + __u8 sleep_clkgate; + __u8 reserved3[533]; + __u8 cpuctrl; +} __packed __aligned(4); + +/* LPMCTRL - bitmasks. */ +/* BESL (Best Effort Service Latency). */ +#define LPMCTRLLL_HIRD GENMASK(7, 4) +/* Last received Remote Wakeup field from LPM Extended Token packet. */ +#define LPMCTRLLH_BREMOTEWAKEUP BIT(8) +/* Reflects value of the lpmnyet bit located in the usbcs[1] register. */ +#define LPMCTRLLH_LPMNYET BIT(16) + +/* LPMCLOCK - bitmasks. */ +/* + * If bit is 1 the controller automatically turns off clock + * (utmisleepm goes to low), else the microprocessor should use + * sleep clock gate register to turn off clock. + */ +#define LPMCLOCK_SLEEP_ENTRY BIT(7) + +/* USBCS - bitmasks. */ +/* Send NYET handshake for the LPM transaction. */ +#define USBCS_LPMNYET BIT(2) +/* Remote wake-up bit. */ +#define USBCS_SIGRSUME BIT(5) +/* Software disconnect bit. */ +#define USBCS_DISCON BIT(6) +/* Indicates that a wakeup pin resumed the controller. */ +#define USBCS_WAKESRC BIT(7) + +/* FIFOCTRL - bitmasks. */ +/* Endpoint number. */ +#define FIFOCTRL_EP GENMASK(3, 0) +/* Direction bit. */ +#define FIFOCTRL_IO_TX BIT(4) +/* FIFO auto bit. */ +#define FIFOCTRL_FIFOAUTO BIT(5) +/* FIFO commit bit. */ +#define FIFOCTRL_FIFOCMIT BIT(6) +/* FIFO access bit. */ +#define FIFOCTRL_FIFOACC BIT(7) + +/* SPEEDCTRL - bitmasks. */ +/* Device works in Full Speed. */ +#define SPEEDCTRL_FS BIT(1) +/* Device works in High Speed. */ +#define SPEEDCTRL_HS BIT(2) +/* Force FS mode. */ +#define SPEEDCTRL_HSDISABLE BIT(7) + +/* CPUCTRL- bitmasks. */ +/* Controller reset bit. */ +#define CPUCTRL_SW_RST BIT(1) + +/** + * struct cdns2_adma_regs - ADMA controller registers. + * @conf: DMA global configuration register. + * @sts: DMA global Status register. + * @reserved1: reserved. + * @ep_sel: DMA endpoint select register. + * @ep_traddr: DMA endpoint transfer ring address register. + * @ep_cfg: DMA endpoint configuration register. + * @ep_cmd: DMA endpoint command register. + * @ep_sts: DMA endpoint status register. + * @reserved2: reserved. + * @ep_sts_en: DMA endpoint status enable register. + * @drbl: DMA doorbell register. + * @ep_ien: DMA endpoint interrupt enable register. + * @ep_ists: DMA endpoint interrupt status register. + * @axim_ctrl: AXI Master Control register. + * @axim_id: AXI Master ID register. + * @reserved3: reserved. + * @axim_cap: AXI Master Wrapper Extended Capability. + * @reserved4: reserved. + * @axim_ctrl0: AXI Master Wrapper Extended Capability Control Register 0. + * @axim_ctrl1: AXI Master Wrapper Extended Capability Control Register 1. + */ +struct cdns2_adma_regs { + __le32 conf; + __le32 sts; + __le32 reserved1[5]; + __le32 ep_sel; + __le32 ep_traddr; + __le32 ep_cfg; + __le32 ep_cmd; + __le32 ep_sts; + __le32 reserved2; + __le32 ep_sts_en; + __le32 drbl; + __le32 ep_ien; + __le32 ep_ists; + __le32 axim_ctrl; + __le32 axim_id; + __le32 reserved3; + __le32 axim_cap; + __le32 reserved4; + __le32 axim_ctrl0; + __le32 axim_ctrl1; +}; + +#define CDNS2_ADMA_REGS_OFFSET 0x400 + +/* DMA_CONF - bitmasks. */ +/* Reset USB device configuration. */ +#define DMA_CONF_CFGRST BIT(0) +/* Singular DMA transfer mode.*/ +#define DMA_CONF_DSING BIT(8) +/* Multiple DMA transfers mode.*/ +#define DMA_CONF_DMULT BIT(9) + +/* DMA_EP_CFG - bitmasks. */ +/* Endpoint enable. */ +#define DMA_EP_CFG_ENABLE BIT(0) + +/* DMA_EP_CMD - bitmasks. */ +/* Endpoint reset. */ +#define DMA_EP_CMD_EPRST BIT(0) +/* Transfer descriptor ready. */ +#define DMA_EP_CMD_DRDY BIT(6) +/* Data flush. */ +#define DMA_EP_CMD_DFLUSH BIT(7) + +/* DMA_EP_STS - bitmasks. */ +/* Interrupt On Complete. */ +#define DMA_EP_STS_IOC BIT(2) +/* Interrupt on Short Packet. */ +#define DMA_EP_STS_ISP BIT(3) +/* Transfer descriptor missing. */ +#define DMA_EP_STS_DESCMIS BIT(4) +/* TRB error. */ +#define DMA_EP_STS_TRBERR BIT(7) +/* DMA busy bit. */ +#define DMA_EP_STS_DBUSY BIT(9) +/* Current Cycle Status. */ +#define DMA_EP_STS_CCS(p) ((p) & BIT(11)) +/* OUT size mismatch. */ +#define DMA_EP_STS_OUTSMM BIT(14) +/* ISO transmission error. */ +#define DMA_EP_STS_ISOERR BIT(15) + +/* DMA_EP_STS_EN - bitmasks. */ +/* OUT transfer missing descriptor enable. */ +#define DMA_EP_STS_EN_DESCMISEN BIT(4) +/* TRB enable. */ +#define DMA_EP_STS_EN_TRBERREN BIT(7) +/* OUT size mismatch enable. */ +#define DMA_EP_STS_EN_OUTSMMEN BIT(14) +/* ISO transmission error enable. */ +#define DMA_EP_STS_EN_ISOERREN BIT(15) + +/* DMA_EP_IEN - bitmasks. */ +#define DMA_EP_IEN(index) (1 << (index)) +#define DMA_EP_IEN_EP_OUT0 BIT(0) +#define DMA_EP_IEN_EP_IN0 BIT(16) + +/* DMA_EP_ISTS - bitmasks. */ +#define DMA_EP_ISTS(index) (1 << (index)) +#define DMA_EP_ISTS_EP_OUT0 BIT(0) +#define DMA_EP_ISTS_EP_IN0 BIT(16) + +#define gadget_to_cdns2_device(g) (container_of(g, struct cdns2_device, gadget)) +#define ep_to_cdns2_ep(ep) (container_of(ep, struct cdns2_endpoint, endpoint)) + +/*-------------------------------------------------------------------------*/ +#define TRBS_PER_SEGMENT 600 +#define ISO_MAX_INTERVAL 8 +#define MAX_TRB_LENGTH BIT(16) +#define MAX_ISO_SIZE 3076 +/* + * To improve performance the TRB buffer pointers can't cross + * 4KB boundaries. + */ +#define TRB_MAX_ISO_BUFF_SHIFT 12 +#define TRB_MAX_ISO_BUFF_SIZE BIT(TRB_MAX_ISO_BUFF_SHIFT) +/* How much data is left before the 4KB boundary? */ +#define TRB_BUFF_LEN_UP_TO_BOUNDARY(addr) (TRB_MAX_ISO_BUFF_SIZE - \ + ((addr) & (TRB_MAX_ISO_BUFF_SIZE - 1))) + +#if TRBS_PER_SEGMENT < 2 +#error "Incorrect TRBS_PER_SEGMENT. Minimal Transfer Ring size is 2." +#endif + +/** + * struct cdns2_trb - represent Transfer Descriptor block. + * @buffer: pointer to buffer data. + * @length: length of data. + * @control: control flags. + * + * This structure describes transfer block handled by DMA module. + */ +struct cdns2_trb { + __le32 buffer; + __le32 length; + __le32 control; +}; + +#define TRB_SIZE (sizeof(struct cdns2_trb)) +/* + * These two extra TRBs are reserved for isochronous transfer + * to inject 0 length packet and extra LINK TRB to synchronize the ISO transfer. + */ +#define TRB_ISO_RESERVED 2 +#define TR_SEG_SIZE (TRB_SIZE * (TRBS_PER_SEGMENT + TRB_ISO_RESERVED)) + +/* TRB bit mask. */ +#define TRB_TYPE_BITMASK GENMASK(15, 10) +#define TRB_TYPE(p) ((p) << 10) +#define TRB_FIELD_TO_TYPE(p) (((p) & TRB_TYPE_BITMASK) >> 10) + +/* TRB type IDs. */ +/* Used for Bulk, Interrupt, ISOC, and control data stage. */ +#define TRB_NORMAL 1 +/* TRB for linking ring segments. */ +#define TRB_LINK 6 + +/* Cycle bit - indicates TRB ownership by driver or hw. */ +#define TRB_CYCLE BIT(0) +/* + * When set to '1', the device will toggle its interpretation of the Cycle bit. + */ +#define TRB_TOGGLE BIT(1) +/* Interrupt on short packet. */ +#define TRB_ISP BIT(2) +/* Chain bit associate this TRB with next one TRB. */ +#define TRB_CHAIN BIT(4) +/* Interrupt on completion. */ +#define TRB_IOC BIT(5) + +/* Transfer_len bitmasks. */ +#define TRB_LEN(p) ((p) & GENMASK(16, 0)) +#define TRB_BURST(p) (((p) << 24) & GENMASK(31, 24)) +#define TRB_FIELD_TO_BURST(p) (((p) & GENMASK(31, 24)) >> 24) + +/* Data buffer pointer bitmasks. */ +#define TRB_BUFFER(p) ((p) & GENMASK(31, 0)) + +/*-------------------------------------------------------------------------*/ +/* Driver numeric constants. */ + +/* Maximum address that can be assigned to device. */ +#define USB_DEVICE_MAX_ADDRESS 127 + +/* One control and 15 IN and 15 OUT endpoints. */ +#define CDNS2_ENDPOINTS_NUM 31 + +#define CDNS2_EP_ZLP_BUF_SIZE 512 + +/*-------------------------------------------------------------------------*/ +/* Used structures. */ + +struct cdns2_device; + +/** + * struct cdns2_ring - transfer ring representation. + * @trbs: pointer to transfer ring. + * @dma: dma address of transfer ring. + * @free_trbs: number of free TRBs in transfer ring. + * @pcs: producer cycle state. + * @ccs: consumer cycle state. + * @enqueue: enqueue index in transfer ring. + * @dequeue: dequeue index in transfer ring. + */ +struct cdns2_ring { + struct cdns2_trb *trbs; + dma_addr_t dma; + int free_trbs; + u8 pcs; + u8 ccs; + int enqueue; + int dequeue; +}; + +/** + * struct cdns2_endpoint - extended device side representation of USB endpoint. + * @endpoint: usb endpoint. + * @pending_list: list of requests queuing on transfer ring. + * @deferred_list: list of requests waiting for queuing on transfer ring. + * @pdev: device associated with endpoint. + * @name: a human readable name e.g. ep1out. + * @ring: transfer ring associated with endpoint. + * @ep_state: state of endpoint. + * @idx: index of endpoint in pdev->eps table. + * @dir: endpoint direction. + * @num: endpoint number (1 - 15). + * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK. + * @interval: interval between packets used for ISOC and Interrupt endpoint. + * @buffering: on-chip buffers assigned to endpoint. + * @trb_burst_size: number of burst used in TRB. + * @skip: Sometimes the controller cannot process isochronous endpoint ring + * quickly enough and it will miss some isoc tds on the ring and + * generate ISO transmition error. + * Driver sets skip flag when receive a ISO transmition error and + * process the missed TDs on the endpoint ring. + * @wa1_set: use WA1. + * @wa1_trb: TRB assigned to WA1. + * @wa1_trb_index: TRB index for WA1. + * @wa1_cycle_bit: correct cycle bit for WA1. + */ +struct cdns2_endpoint { + struct usb_ep endpoint; + struct list_head pending_list; + struct list_head deferred_list; + + struct cdns2_device *pdev; + char name[20]; + + struct cdns2_ring ring; + +#define EP_ENABLED BIT(0) +#define EP_STALLED BIT(1) +#define EP_STALL_PENDING BIT(2) +#define EP_WEDGE BIT(3) +#define EP_CLAIMED BIT(4) +#define EP_RING_FULL BIT(5) +#define EP_DEFERRED_DRDY BIT(6) + + u32 ep_state; + + u8 idx; + u8 dir; + u8 num; + u8 type; + int interval; + u8 buffering; + u8 trb_burst_size; + bool skip; + + unsigned int wa1_set:1; + struct cdns2_trb *wa1_trb; + unsigned int wa1_trb_index; + unsigned int wa1_cycle_bit:1; +}; + +/** + * struct cdns2_request - extended device side representation of usb_request + * object. + * @request: generic usb_request object describing single I/O request. + * @pep: extended representation of usb_ep object. + * @trb: the first TRB association with this request. + * @start_trb: number of the first TRB in transfer ring. + * @end_trb: number of the last TRB in transfer ring. + * @list: used for queuing request in lists. + * @finished_trb: number of trb has already finished per request. + * @num_of_trb: how many trbs are associated with request. + */ +struct cdns2_request { + struct usb_request request; + struct cdns2_endpoint *pep; + struct cdns2_trb *trb; + int start_trb; + int end_trb; + struct list_head list; + int finished_trb; + int num_of_trb; +}; + +#define to_cdns2_request(r) (container_of(r, struct cdns2_request, request)) + +/* Stages used during enumeration process.*/ +#define CDNS2_SETUP_STAGE 0x0 +#define CDNS2_DATA_STAGE 0x1 +#define CDNS2_STATUS_STAGE 0x2 + +/** + * struct cdns2_device - represent USB device. + * @dev: pointer to device structure associated whit this controller. + * @gadget: device side representation of the peripheral controller. + * @gadget_driver: pointer to the gadget driver. + * @lock: for synchronizing. + * @irq: interrupt line number. + * @regs: base address for registers + * @usb_regs: base address for common USB registers. + * @ep0_regs: base address for endpoint 0 related registers. + * @epx_regs: base address for all none control endpoint registers. + * @interrupt_regs: base address for interrupt handling related registers. + * @adma_regs: base address for ADMA registers. + * @eps_dma_pool: endpoint Transfer Ring pool. + * @setup: used while processing usb control requests. + * @ep0_preq: private request used while handling EP0. + * @ep0_stage: ep0 stage during enumeration process. + * @zlp_buf: zlp buffer. + * @dev_address: device address assigned by host. + * @eps: array of objects describing endpoints. + * @selected_ep: actually selected endpoint. It's used only to improve + * performance by limiting access to dma_ep_sel register. + * @is_selfpowered: device is self powered. + * @may_wakeup: allows device to remote wakeup the host. + * @status_completion_no_call: indicate that driver is waiting for status + * stage completion. It's used in deferred SET_CONFIGURATION request. + * @in_lpm: indicate the controller is in low power mode. + * @pending_status_wq: workqueue handling status stage for deferred requests. + * @pending_status_request: request for which status stage was deferred. + * @eps_supported: endpoints supported by controller in form: + * bit: 0 - ep0, 1 - epOut1, 2 - epIn1, 3 - epOut2 ... + * @burst_opt: array with the best burst size value for different TRB size. + * @onchip_tx_buf: size of transmit on-chip buffer in KB. + * @onchip_rx_buf: size of receive on-chip buffer in KB. + */ +struct cdns2_device { + struct device *dev; + struct usb_gadget gadget; + struct usb_gadget_driver *gadget_driver; + + /* generic spin-lock for drivers */ + spinlock_t lock; + int irq; + void __iomem *regs; + struct cdns2_usb_regs __iomem *usb_regs; + struct cdns2_ep0_regs __iomem *ep0_regs; + struct cdns2_epx_regs __iomem *epx_regs; + struct cdns2_interrupt_regs __iomem *interrupt_regs; + struct cdns2_adma_regs __iomem *adma_regs; + struct dma_pool *eps_dma_pool; + struct usb_ctrlrequest setup; + struct cdns2_request ep0_preq; + u8 ep0_stage; + void *zlp_buf; + u8 dev_address; + struct cdns2_endpoint eps[CDNS2_ENDPOINTS_NUM]; + u32 selected_ep; + bool is_selfpowered; + bool may_wakeup; + bool status_completion_no_call; + bool in_lpm; + struct work_struct pending_status_wq; + struct usb_request *pending_status_request; + u32 eps_supported; + u8 burst_opt[MAX_ISO_SIZE + 1]; + + /*in KB */ + u16 onchip_tx_buf; + u16 onchip_rx_buf; +}; + +#define CDNS2_IF_EP_EXIST(pdev, ep_num, dir) \ + ((pdev)->eps_supported & \ + (BIT(ep_num) << ((dir) ? 0 : 16))) + +dma_addr_t cdns2_trb_virt_to_dma(struct cdns2_endpoint *pep, + struct cdns2_trb *trb); +void cdns2_pending_setup_status_handler(struct work_struct *work); +void cdns2_select_ep(struct cdns2_device *pdev, u32 ep); +struct cdns2_request *cdns2_next_preq(struct list_head *list); +struct usb_request *cdns2_gadget_ep_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags); +void cdns2_gadget_ep_free_request(struct usb_ep *ep, + struct usb_request *request); +int cdns2_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request); +void cdns2_gadget_giveback(struct cdns2_endpoint *pep, + struct cdns2_request *priv_req, + int status); +void cdns2_init_ep0(struct cdns2_device *pdev, struct cdns2_endpoint *pep); +void cdns2_ep0_config(struct cdns2_device *pdev); +void cdns2_handle_ep0_interrupt(struct cdns2_device *pdev, int dir); +void cdns2_handle_setup_packet(struct cdns2_device *pdev); +int cdns2_gadget_resume(struct cdns2_device *pdev, bool hibernated); +int cdns2_gadget_suspend(struct cdns2_device *pdev); +void cdns2_gadget_remove(struct cdns2_device *pdev); +int cdns2_gadget_init(struct cdns2_device *pdev); +void set_reg_bit_8(void __iomem *ptr, u8 mask); +int cdns2_halt_endpoint(struct cdns2_device *pdev, struct cdns2_endpoint *pep, + int value); + +#endif /* __LINUX_CDNS2_GADGET */ -- cgit v1.2.3 From 3eb1f1efe2045e7083048f4cc4257d0df51899b8 Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Fri, 2 Jun 2023 06:26:42 -0400 Subject: usb: cdns2: Add main part of Cadence USBHS driver This patch introduces the main part of Cadence USBHS driver to Linux kernel. To reduce the patch size a little bit, the header file gadget.h was intentionally added as separate patch. The Cadence USB 2.0 Controller is a highly configurable IP Core which supports both full and high speed data transfer. The current driver has been validated with FPGA platform. We have support for PCIe bus, which is used on FPGA prototyping. Signed-off-by: Pawel Laszczak Message-ID: <20230602102644.77470-3-pawell@cadence.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/Kconfig | 2 + drivers/usb/gadget/udc/Makefile | 1 + drivers/usb/gadget/udc/cdns2/Kconfig | 11 + drivers/usb/gadget/udc/cdns2/Makefile | 5 + drivers/usb/gadget/udc/cdns2/cdns2-ep0.c | 637 +++++++ drivers/usb/gadget/udc/cdns2/cdns2-gadget.c | 2424 +++++++++++++++++++++++++++ drivers/usb/gadget/udc/cdns2/cdns2-pci.c | 138 ++ 7 files changed, 3218 insertions(+) create mode 100644 drivers/usb/gadget/udc/cdns2/Kconfig create mode 100644 drivers/usb/gadget/udc/cdns2/Makefile create mode 100644 drivers/usb/gadget/udc/cdns2/cdns2-ep0.c create mode 100644 drivers/usb/gadget/udc/cdns2/cdns2-gadget.c create mode 100644 drivers/usb/gadget/udc/cdns2/cdns2-pci.c diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index 83cae6bb12eb..aae1787320d4 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -463,6 +463,8 @@ config USB_ASPEED_UDC source "drivers/usb/gadget/udc/aspeed-vhub/Kconfig" +source "drivers/usb/gadget/udc/cdns2/Kconfig" + # # LAST -- dummy/emulated controller # diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile index ee569f63c74a..b52f93e9c61d 100644 --- a/drivers/usb/gadget/udc/Makefile +++ b/drivers/usb/gadget/udc/Makefile @@ -42,3 +42,4 @@ obj-$(CONFIG_USB_ASPEED_VHUB) += aspeed-vhub/ obj-$(CONFIG_USB_ASPEED_UDC) += aspeed_udc.o obj-$(CONFIG_USB_BDC_UDC) += bdc/ obj-$(CONFIG_USB_MAX3420_UDC) += max3420_udc.o +obj-$(CONFIG_USB_CDNS2_UDC) += cdns2/ diff --git a/drivers/usb/gadget/udc/cdns2/Kconfig b/drivers/usb/gadget/udc/cdns2/Kconfig new file mode 100644 index 000000000000..c07d353903ea --- /dev/null +++ b/drivers/usb/gadget/udc/cdns2/Kconfig @@ -0,0 +1,11 @@ +config USB_CDNS2_UDC + tristate "Cadence USBHS Device Controller" + depends on USB_PCI && ACPI && HAS_DMA + help + Cadence USBHS Device controller is a PCI based USB peripheral + controller which supports both full and high speed USB 2.0 + data transfers. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "cdns2-udc-pci.ko" and to + force all gadget drivers to also be dynamically linked. diff --git a/drivers/usb/gadget/udc/cdns2/Makefile b/drivers/usb/gadget/udc/cdns2/Makefile new file mode 100644 index 000000000000..7c746e6d53c2 --- /dev/null +++ b/drivers/usb/gadget/udc/cdns2/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 +# define_trace.h needs to know how to find our header + +obj-$(CONFIG_USB_CDNS2_UDC) += cdns2-udc-pci.o +cdns2-udc-pci-$(CONFIG_USB_CDNS2_UDC) += cdns2-pci.o cdns2-gadget.o cdns2-ep0.o diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-ep0.c b/drivers/usb/gadget/udc/cdns2/cdns2-ep0.c new file mode 100644 index 000000000000..133d5acaf502 --- /dev/null +++ b/drivers/usb/gadget/udc/cdns2/cdns2-ep0.c @@ -0,0 +1,637 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cadence USBHS-DEV driver. + * + * Copyright (C) 2023 Cadence Design Systems. + * + * Authors: Pawel Laszczak + */ + +#include +#include + +#include "cdns2-gadget.h" + +static struct usb_endpoint_descriptor cdns2_gadget_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = cpu_to_le16(64) +}; + +static int cdns2_w_index_to_ep_index(u16 wIndex) +{ + if (!(wIndex & USB_ENDPOINT_NUMBER_MASK)) + return 0; + + return ((wIndex & USB_ENDPOINT_NUMBER_MASK) * 2) + + (wIndex & USB_ENDPOINT_DIR_MASK ? 1 : 0) - 1; +} + +static bool cdns2_check_new_setup(struct cdns2_device *pdev) +{ + u8 reg; + + reg = readb(&pdev->ep0_regs->cs); + + return !!(reg & EP0CS_CHGSET); +} + +static void cdns2_ep0_enqueue(struct cdns2_device *pdev, dma_addr_t dma_addr, + unsigned int length, int zlp) +{ + struct cdns2_adma_regs __iomem *regs = pdev->adma_regs; + struct cdns2_endpoint *pep = &pdev->eps[0]; + struct cdns2_ring *ring = &pep->ring; + + ring->trbs[0].buffer = cpu_to_le32(TRB_BUFFER(dma_addr)); + ring->trbs[0].length = cpu_to_le32(TRB_LEN(length)); + + if (zlp) { + ring->trbs[0].control = cpu_to_le32(TRB_CYCLE | + TRB_TYPE(TRB_NORMAL)); + ring->trbs[1].buffer = cpu_to_le32(TRB_BUFFER(dma_addr)); + ring->trbs[1].length = cpu_to_le32(TRB_LEN(0)); + ring->trbs[1].control = cpu_to_le32(TRB_CYCLE | TRB_IOC | + TRB_TYPE(TRB_NORMAL)); + } else { + ring->trbs[0].control = cpu_to_le32(TRB_CYCLE | TRB_IOC | + TRB_TYPE(TRB_NORMAL)); + ring->trbs[1].control = 0; + } + + if (!pep->dir) + writel(0, &pdev->ep0_regs->rxbc); + + cdns2_select_ep(pdev, pep->dir); + + writel(DMA_EP_STS_TRBERR, ®s->ep_sts); + writel(pep->ring.dma, ®s->ep_traddr); + + writel(DMA_EP_CMD_DRDY, ®s->ep_cmd); +} + +static int cdns2_ep0_delegate_req(struct cdns2_device *pdev) +{ + int ret; + + spin_unlock(&pdev->lock); + ret = pdev->gadget_driver->setup(&pdev->gadget, &pdev->setup); + spin_lock(&pdev->lock); + + return ret; +} + +static void cdns2_ep0_stall(struct cdns2_device *pdev) +{ + struct cdns2_endpoint *pep = &pdev->eps[0]; + struct cdns2_request *preq; + + preq = cdns2_next_preq(&pep->pending_list); + set_reg_bit_8(&pdev->ep0_regs->cs, EP0CS_DSTALL); + + if (pdev->ep0_stage == CDNS2_DATA_STAGE && preq) + cdns2_gadget_giveback(pep, preq, -ECONNRESET); + else if (preq) + list_del_init(&preq->list); + + pdev->ep0_stage = CDNS2_SETUP_STAGE; + pep->ep_state |= EP_STALLED; +} + +static void cdns2_status_stage(struct cdns2_device *pdev) +{ + struct cdns2_endpoint *pep = &pdev->eps[0]; + struct cdns2_request *preq; + + preq = cdns2_next_preq(&pep->pending_list); + if (preq) + list_del_init(&preq->list); + + pdev->ep0_stage = CDNS2_SETUP_STAGE; + writeb(EP0CS_HSNAK, &pdev->ep0_regs->cs); +} + +static int cdns2_req_ep0_set_configuration(struct cdns2_device *pdev, + struct usb_ctrlrequest *ctrl_req) +{ + enum usb_device_state state = pdev->gadget.state; + u32 config = le16_to_cpu(ctrl_req->wValue); + int ret; + + if (state < USB_STATE_ADDRESS) { + dev_err(pdev->dev, "Set Configuration - bad device state\n"); + return -EINVAL; + } + + ret = cdns2_ep0_delegate_req(pdev); + if (ret) + return ret; + + if (!config) + usb_gadget_set_state(&pdev->gadget, USB_STATE_ADDRESS); + + return 0; +} + +static int cdns2_req_ep0_set_address(struct cdns2_device *pdev, u32 addr) +{ + enum usb_device_state device_state = pdev->gadget.state; + u8 reg; + + if (addr > USB_DEVICE_MAX_ADDRESS) { + dev_err(pdev->dev, + "Device address (%d) cannot be greater than %d\n", + addr, USB_DEVICE_MAX_ADDRESS); + return -EINVAL; + } + + if (device_state == USB_STATE_CONFIGURED) { + dev_err(pdev->dev, + "can't set_address from configured state\n"); + return -EINVAL; + } + + reg = readb(&pdev->usb_regs->fnaddr); + pdev->dev_address = reg; + + usb_gadget_set_state(&pdev->gadget, + (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT)); + + return 0; +} + +static int cdns2_req_ep0_handle_status(struct cdns2_device *pdev, + struct usb_ctrlrequest *ctrl) +{ + struct cdns2_endpoint *pep; + __le16 *response_pkt; + u16 status = 0; + int ep_sts; + u32 recip; + + recip = ctrl->bRequestType & USB_RECIP_MASK; + + switch (recip) { + case USB_RECIP_DEVICE: + status = pdev->gadget.is_selfpowered; + status |= pdev->may_wakeup << USB_DEVICE_REMOTE_WAKEUP; + break; + case USB_RECIP_INTERFACE: + return cdns2_ep0_delegate_req(pdev); + case USB_RECIP_ENDPOINT: + ep_sts = cdns2_w_index_to_ep_index(le16_to_cpu(ctrl->wIndex)); + pep = &pdev->eps[ep_sts]; + + if (pep->ep_state & EP_STALLED) + status = BIT(USB_ENDPOINT_HALT); + break; + default: + return -EINVAL; + } + + put_unaligned_le16(status, (__le16 *)pdev->ep0_preq.request.buf); + + cdns2_ep0_enqueue(pdev, pdev->ep0_preq.request.dma, + sizeof(*response_pkt), 0); + + return 0; +} + +static int cdns2_ep0_handle_feature_device(struct cdns2_device *pdev, + struct usb_ctrlrequest *ctrl, + int set) +{ + enum usb_device_state state; + enum usb_device_speed speed; + int ret = 0; + u32 wValue; + u16 tmode; + + wValue = le16_to_cpu(ctrl->wValue); + state = pdev->gadget.state; + speed = pdev->gadget.speed; + + switch (wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + pdev->may_wakeup = !!set; + break; + case USB_DEVICE_TEST_MODE: + if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH) + return -EINVAL; + + tmode = le16_to_cpu(ctrl->wIndex); + + if (!set || (tmode & 0xff) != 0) + return -EINVAL; + + tmode >>= 8; + switch (tmode) { + case USB_TEST_J: + case USB_TEST_K: + case USB_TEST_SE0_NAK: + case USB_TEST_PACKET: + /* + * The USBHS controller automatically handles the + * Set_Feature(testmode) request. Standard test modes + * that use values of test mode selector from + * 01h to 04h (Test_J, Test_K, Test_SE0_NAK, + * Test_Packet) are supported by the + * controller(HS - ack, FS - stall). + */ + break; + default: + ret = -EINVAL; + } + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int cdns2_ep0_handle_feature_intf(struct cdns2_device *pdev, + struct usb_ctrlrequest *ctrl, + int set) +{ + int ret = 0; + u32 wValue; + + wValue = le16_to_cpu(ctrl->wValue); + + switch (wValue) { + case USB_INTRF_FUNC_SUSPEND: + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int cdns2_ep0_handle_feature_endpoint(struct cdns2_device *pdev, + struct usb_ctrlrequest *ctrl, + int set) +{ + struct cdns2_endpoint *pep; + u8 wValue; + + wValue = le16_to_cpu(ctrl->wValue); + pep = &pdev->eps[cdns2_w_index_to_ep_index(le16_to_cpu(ctrl->wIndex))]; + + if (wValue != USB_ENDPOINT_HALT) + return -EINVAL; + + if (!(le16_to_cpu(ctrl->wIndex) & ~USB_DIR_IN)) + return 0; + + switch (wValue) { + case USB_ENDPOINT_HALT: + if (set || !(pep->ep_state & EP_WEDGE)) + return cdns2_halt_endpoint(pdev, pep, set); + break; + default: + dev_warn(pdev->dev, "WARN Incorrect wValue %04x\n", wValue); + return -EINVAL; + } + + return 0; +} + +static int cdns2_req_ep0_handle_feature(struct cdns2_device *pdev, + struct usb_ctrlrequest *ctrl, + int set) +{ + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + return cdns2_ep0_handle_feature_device(pdev, ctrl, set); + case USB_RECIP_INTERFACE: + return cdns2_ep0_handle_feature_intf(pdev, ctrl, set); + case USB_RECIP_ENDPOINT: + return cdns2_ep0_handle_feature_endpoint(pdev, ctrl, set); + default: + return -EINVAL; + } +} + +static int cdns2_ep0_std_request(struct cdns2_device *pdev) +{ + struct usb_ctrlrequest *ctrl = &pdev->setup; + int ret; + + switch (ctrl->bRequest) { + case USB_REQ_SET_ADDRESS: + ret = cdns2_req_ep0_set_address(pdev, + le16_to_cpu(ctrl->wValue)); + break; + case USB_REQ_SET_CONFIGURATION: + ret = cdns2_req_ep0_set_configuration(pdev, ctrl); + break; + case USB_REQ_GET_STATUS: + ret = cdns2_req_ep0_handle_status(pdev, ctrl); + break; + case USB_REQ_CLEAR_FEATURE: + ret = cdns2_req_ep0_handle_feature(pdev, ctrl, 0); + break; + case USB_REQ_SET_FEATURE: + ret = cdns2_req_ep0_handle_feature(pdev, ctrl, 1); + break; + default: + ret = cdns2_ep0_delegate_req(pdev); + break; + } + + return ret; +} + +static void __pending_setup_status_handler(struct cdns2_device *pdev) +{ + struct usb_request *request = pdev->pending_status_request; + + if (pdev->status_completion_no_call && request && request->complete) { + request->complete(&pdev->eps[0].endpoint, request); + pdev->status_completion_no_call = 0; + } +} + +void cdns2_pending_setup_status_handler(struct work_struct *work) +{ + struct cdns2_device *pdev = container_of(work, struct cdns2_device, + pending_status_wq); + unsigned long flags; + + spin_lock_irqsave(&pdev->lock, flags); + __pending_setup_status_handler(pdev); + spin_unlock_irqrestore(&pdev->lock, flags); +} + +void cdns2_handle_setup_packet(struct cdns2_device *pdev) +{ + struct usb_ctrlrequest *ctrl = &pdev->setup; + struct cdns2_endpoint *pep = &pdev->eps[0]; + struct cdns2_request *preq; + int ret = 0; + u16 len; + u8 reg; + int i; + + writeb(EP0CS_CHGSET, &pdev->ep0_regs->cs); + + for (i = 0; i < 8; i++) + ((u8 *)&pdev->setup)[i] = readb(&pdev->ep0_regs->setupdat[i]); + + /* + * If SETUP packet was modified while reading just simple ignore it. + * The new one will be handled latter. + */ + if (cdns2_check_new_setup(pdev)) + return; + + if (!pdev->gadget_driver) + goto out; + + if (pdev->gadget.state == USB_STATE_NOTATTACHED) { + dev_err(pdev->dev, "ERR: Setup detected in unattached state\n"); + ret = -EINVAL; + goto out; + } + + pep = &pdev->eps[0]; + + /* Halt for Ep0 is cleared automatically when SETUP packet arrives. */ + pep->ep_state &= ~EP_STALLED; + + if (!list_empty(&pep->pending_list)) { + preq = cdns2_next_preq(&pep->pending_list); + cdns2_gadget_giveback(pep, preq, -ECONNRESET); + } + + len = le16_to_cpu(ctrl->wLength); + if (len) + pdev->ep0_stage = CDNS2_DATA_STAGE; + else + pdev->ep0_stage = CDNS2_STATUS_STAGE; + + pep->dir = ctrl->bRequestType & USB_DIR_IN; + + /* + * SET_ADDRESS request is acknowledged automatically by controller and + * in the worse case driver may not notice this request. To check + * whether this request has been processed driver can use + * fnaddr register. + */ + reg = readb(&pdev->usb_regs->fnaddr); + if (pdev->setup.bRequest != USB_REQ_SET_ADDRESS && + pdev->dev_address != reg) + cdns2_req_ep0_set_address(pdev, reg); + + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) + ret = cdns2_ep0_std_request(pdev); + else + ret = cdns2_ep0_delegate_req(pdev); + + if (ret == USB_GADGET_DELAYED_STATUS) + return; + +out: + if (ret < 0) + cdns2_ep0_stall(pdev); + else if (pdev->ep0_stage == CDNS2_STATUS_STAGE) + cdns2_status_stage(pdev); +} + +static void cdns2_transfer_completed(struct cdns2_device *pdev) +{ + struct cdns2_endpoint *pep = &pdev->eps[0]; + + if (!list_empty(&pep->pending_list)) { + struct cdns2_request *preq; + + preq = cdns2_next_preq(&pep->pending_list); + + preq->request.actual = + TRB_LEN(le32_to_cpu(pep->ring.trbs->length)); + cdns2_gadget_giveback(pep, preq, 0); + } + + cdns2_status_stage(pdev); +} + +void cdns2_handle_ep0_interrupt(struct cdns2_device *pdev, int dir) +{ + u32 ep_sts_reg; + + cdns2_select_ep(pdev, dir); + + ep_sts_reg = readl(&pdev->adma_regs->ep_sts); + writel(ep_sts_reg, &pdev->adma_regs->ep_sts); + + __pending_setup_status_handler(pdev); + + if ((ep_sts_reg & DMA_EP_STS_IOC) || (ep_sts_reg & DMA_EP_STS_ISP)) { + pdev->eps[0].dir = dir; + cdns2_transfer_completed(pdev); + } +} + +/* + * Function shouldn't be called by gadget driver, + * endpoint 0 is allways active. + */ +static int cdns2_gadget_ep0_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + return -EINVAL; +} + +/* + * Function shouldn't be called by gadget driver, + * endpoint 0 is allways active. + */ +static int cdns2_gadget_ep0_disable(struct usb_ep *ep) +{ + return -EINVAL; +} + +static int cdns2_gadget_ep0_set_halt(struct usb_ep *ep, int value) +{ + struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep); + struct cdns2_device *pdev = pep->pdev; + unsigned long flags; + + if (!value) + return 0; + + spin_lock_irqsave(&pdev->lock, flags); + cdns2_ep0_stall(pdev); + spin_unlock_irqrestore(&pdev->lock, flags); + + return 0; +} + +static int cdns2_gadget_ep0_set_wedge(struct usb_ep *ep) +{ + return cdns2_gadget_ep0_set_halt(ep, 1); +} + +static int cdns2_gadget_ep0_queue(struct usb_ep *ep, + struct usb_request *request, + gfp_t gfp_flags) +{ + struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep); + struct cdns2_device *pdev = pep->pdev; + struct cdns2_request *preq; + unsigned long flags; + u8 zlp = 0; + int ret; + + spin_lock_irqsave(&pdev->lock, flags); + + preq = to_cdns2_request(request); + + /* Cancel the request if controller receive new SETUP packet. */ + if (cdns2_check_new_setup(pdev)) { + spin_unlock_irqrestore(&pdev->lock, flags); + return -ECONNRESET; + } + + /* Send STATUS stage. Should be called only for SET_CONFIGURATION. */ + if (pdev->ep0_stage == CDNS2_STATUS_STAGE) { + cdns2_status_stage(pdev); + + request->actual = 0; + pdev->status_completion_no_call = true; + pdev->pending_status_request = request; + usb_gadget_set_state(&pdev->gadget, USB_STATE_CONFIGURED); + spin_unlock_irqrestore(&pdev->lock, flags); + + /* + * Since there is no completion interrupt for status stage, + * it needs to call ->completion in software after + * cdns2_gadget_ep0_queue is back. + */ + queue_work(system_freezable_wq, &pdev->pending_status_wq); + return 0; + } + + if (!list_empty(&pep->pending_list)) { + dev_err(pdev->dev, + "can't handle multiple requests for ep0\n"); + spin_unlock_irqrestore(&pdev->lock, flags); + return -EBUSY; + } + + ret = usb_gadget_map_request_by_dev(pdev->dev, request, pep->dir); + if (ret) { + spin_unlock_irqrestore(&pdev->lock, flags); + dev_err(pdev->dev, "failed to map request\n"); + return -EINVAL; + } + + request->status = -EINPROGRESS; + list_add_tail(&preq->list, &pep->pending_list); + + if (request->zero && request->length && + (request->length % ep->maxpacket == 0)) + zlp = 1; + + cdns2_ep0_enqueue(pdev, request->dma, request->length, zlp); + + spin_unlock_irqrestore(&pdev->lock, flags); + + return 0; +} + +static const struct usb_ep_ops cdns2_gadget_ep0_ops = { + .enable = cdns2_gadget_ep0_enable, + .disable = cdns2_gadget_ep0_disable, + .alloc_request = cdns2_gadget_ep_alloc_request, + .free_request = cdns2_gadget_ep_free_request, + .queue = cdns2_gadget_ep0_queue, + .dequeue = cdns2_gadget_ep_dequeue, + .set_halt = cdns2_gadget_ep0_set_halt, + .set_wedge = cdns2_gadget_ep0_set_wedge, +}; + +void cdns2_ep0_config(struct cdns2_device *pdev) +{ + struct cdns2_endpoint *pep; + + pep = &pdev->eps[0]; + + if (!list_empty(&pep->pending_list)) { + struct cdns2_request *preq; + + preq = cdns2_next_preq(&pep->pending_list); + list_del_init(&preq->list); + } + + writeb(EP0_FIFO_AUTO, &pdev->ep0_regs->fifo); + cdns2_select_ep(pdev, USB_DIR_OUT); + writel(DMA_EP_CFG_ENABLE, &pdev->adma_regs->ep_cfg); + + writeb(EP0_FIFO_IO_TX | EP0_FIFO_AUTO, &pdev->ep0_regs->fifo); + cdns2_select_ep(pdev, USB_DIR_IN); + writel(DMA_EP_CFG_ENABLE, &pdev->adma_regs->ep_cfg); + + writeb(pdev->gadget.ep0->maxpacket, &pdev->ep0_regs->maxpack); + writel(DMA_EP_IEN_EP_OUT0 | DMA_EP_IEN_EP_IN0, + &pdev->adma_regs->ep_ien); +} + +void cdns2_init_ep0(struct cdns2_device *pdev, + struct cdns2_endpoint *pep) +{ + u16 maxpacket = le16_to_cpu(cdns2_gadget_ep0_desc.wMaxPacketSize); + + usb_ep_set_maxpacket_limit(&pep->endpoint, maxpacket); + + pep->endpoint.ops = &cdns2_gadget_ep0_ops; + pep->endpoint.desc = &cdns2_gadget_ep0_desc; + pep->endpoint.caps.type_control = true; + pep->endpoint.caps.dir_in = true; + pep->endpoint.caps.dir_out = true; + + pdev->gadget.ep0 = &pep->endpoint; +} diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c new file mode 100644 index 000000000000..66680e189d97 --- /dev/null +++ b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c @@ -0,0 +1,2424 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cadence USBHS-DEV Driver - gadget side. + * + * Copyright (C) 2023 Cadence Design Systems. + * + * Authors: Pawel Laszczak + */ + +/* + * Work around 1: + * At some situations, the controller may get stale data address in TRB + * at below sequences: + * 1. Controller read TRB includes data address + * 2. Software updates TRBs includes data address and Cycle bit + * 3. Controller read TRB which includes Cycle bit + * 4. DMA run with stale data address + * + * To fix this problem, driver needs to make the first TRB in TD as invalid. + * After preparing all TRBs driver needs to check the position of DMA and + * if the DMA point to the first just added TRB and doorbell is 1, + * then driver must defer making this TRB as valid. This TRB will be make + * as valid during adding next TRB only if DMA is stopped or at TRBERR + * interrupt. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "cdns2-gadget.h" + +/** + * set_reg_bit_32 - set bit in given 32 bits register. + * @ptr: register address. + * @mask: bits to set. + */ +static void set_reg_bit_32(void __iomem *ptr, u32 mask) +{ + mask = readl(ptr) | mask; + writel(mask, ptr); +} + +/* + * clear_reg_bit_32 - clear bit in given 32 bits register. + * @ptr: register address. + * @mask: bits to clear. + */ +static void clear_reg_bit_32(void __iomem *ptr, u32 mask) +{ + mask = readl(ptr) & ~mask; + writel(mask, ptr); +} + +/* Clear bit in given 8 bits register. */ +static void clear_reg_bit_8(void __iomem *ptr, u8 mask) +{ + mask = readb(ptr) & ~mask; + writeb(mask, ptr); +} + +/* Set bit in given 16 bits register. */ +void set_reg_bit_8(void __iomem *ptr, u8 mask) +{ + mask = readb(ptr) | mask; + writeb(mask, ptr); +} + +static int cdns2_get_dma_pos(struct cdns2_device *pdev, + struct cdns2_endpoint *pep) +{ + int dma_index; + + dma_index = readl(&pdev->adma_regs->ep_traddr) - pep->ring.dma; + + return dma_index / TRB_SIZE; +} + +/* Get next private request from list. */ +struct cdns2_request *cdns2_next_preq(struct list_head *list) +{ + return list_first_entry_or_null(list, struct cdns2_request, list); +} + +void cdns2_select_ep(struct cdns2_device *pdev, u32 ep) +{ + if (pdev->selected_ep == ep) + return; + + pdev->selected_ep = ep; + writel(ep, &pdev->adma_regs->ep_sel); +} + +dma_addr_t cdns2_trb_virt_to_dma(struct cdns2_endpoint *pep, + struct cdns2_trb *trb) +{ + u32 offset = (char *)trb - (char *)pep->ring.trbs; + + return pep->ring.dma + offset; +} + +static void cdns2_free_tr_segment(struct cdns2_endpoint *pep) +{ + struct cdns2_device *pdev = pep->pdev; + struct cdns2_ring *ring = &pep->ring; + + if (pep->ring.trbs) { + dma_pool_free(pdev->eps_dma_pool, ring->trbs, ring->dma); + memset(ring, 0, sizeof(*ring)); + } +} + +/* Allocates Transfer Ring segment. */ +static int cdns2_alloc_tr_segment(struct cdns2_endpoint *pep) +{ + struct cdns2_device *pdev = pep->pdev; + struct cdns2_trb *link_trb; + struct cdns2_ring *ring; + + ring = &pep->ring; + + if (!ring->trbs) { + ring->trbs = dma_pool_alloc(pdev->eps_dma_pool, + GFP_DMA32 | GFP_ATOMIC, + &ring->dma); + if (!ring->trbs) + return -ENOMEM; + } + + memset(ring->trbs, 0, TR_SEG_SIZE); + + if (!pep->num) + return 0; + + /* Initialize the last TRB as Link TRB */ + link_trb = (ring->trbs + (TRBS_PER_SEGMENT - 1)); + link_trb->buffer = cpu_to_le32(TRB_BUFFER(ring->dma)); + link_trb->control = cpu_to_le32(TRB_CYCLE | TRB_TYPE(TRB_LINK) | + TRB_TOGGLE); + + return 0; +} + +/* + * Stalls and flushes selected endpoint. + * Endpoint must be selected before invoking this function. + */ +static void cdns2_ep_stall_flush(struct cdns2_endpoint *pep) +{ + struct cdns2_device *pdev = pep->pdev; + int val; + + writel(DMA_EP_CMD_DFLUSH, &pdev->adma_regs->ep_cmd); + + /* Wait for DFLUSH cleared. */ + readl_poll_timeout_atomic(&pdev->adma_regs->ep_cmd, val, + !(val & DMA_EP_CMD_DFLUSH), 1, 1000); + pep->ep_state |= EP_STALLED; + pep->ep_state &= ~EP_STALL_PENDING; +} + +/* + * Increment a trb index. + * + * The index should never point to the last link TRB in TR. After incrementing, + * if it point to the link TRB, wrap around to the beginning and revert + * cycle state bit. The link TRB is always at the last TRB entry. + */ +static void cdns2_ep_inc_trb(int *index, u8 *cs, int trb_in_seg) +{ + (*index)++; + if (*index == (trb_in_seg - 1)) { + *index = 0; + *cs ^= 1; + } +} + +static void cdns2_ep_inc_enq(struct cdns2_ring *ring) +{ + ring->free_trbs--; + cdns2_ep_inc_trb(&ring->enqueue, &ring->pcs, TRBS_PER_SEGMENT); +} + +static void cdns2_ep_inc_deq(struct cdns2_ring *ring) +{ + ring->free_trbs++; + cdns2_ep_inc_trb(&ring->dequeue, &ring->ccs, TRBS_PER_SEGMENT); +} + +/* + * Enable/disable LPM. + * + * If bit USBCS_LPMNYET is not set and device receive Extended Token packet, + * then controller answer with ACK handshake. + * If bit USBCS_LPMNYET is set and device receive Extended Token packet, + * then controller answer with NYET handshake. + */ +static void cdns2_enable_l1(struct cdns2_device *pdev, int enable) +{ + if (enable) { + clear_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_LPMNYET); + writeb(LPMCLOCK_SLEEP_ENTRY, &pdev->usb_regs->lpmclock); + } else { + set_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_LPMNYET); + } +} + +static enum usb_device_speed cdns2_get_speed(struct cdns2_device *pdev) +{ + u8 speed = readb(&pdev->usb_regs->speedctrl); + + if (speed & SPEEDCTRL_HS) + return USB_SPEED_HIGH; + else if (speed & SPEEDCTRL_FS) + return USB_SPEED_FULL; + + return USB_SPEED_UNKNOWN; +} + +static struct cdns2_trb *cdns2_next_trb(struct cdns2_endpoint *pep, + struct cdns2_trb *trb) +{ + if (trb == (pep->ring.trbs + (TRBS_PER_SEGMENT - 1))) + return pep->ring.trbs; + else + return ++trb; +} + +void cdns2_gadget_giveback(struct cdns2_endpoint *pep, + struct cdns2_request *preq, + int status) +{ + struct usb_request *request = &preq->request; + struct cdns2_device *pdev = pep->pdev; + + list_del_init(&preq->list); + + if (request->status == -EINPROGRESS) + request->status = status; + + usb_gadget_unmap_request_by_dev(pdev->dev, request, pep->dir); + + /* All TRBs have finished, clear the counter. */ + preq->finished_trb = 0; + + if (request->complete) { + spin_unlock(&pdev->lock); + usb_gadget_giveback_request(&pep->endpoint, request); + spin_lock(&pdev->lock); + } + + if (request->buf == pdev->zlp_buf) + cdns2_gadget_ep_free_request(&pep->endpoint, request); +} + +static void cdns2_wa1_restore_cycle_bit(struct cdns2_endpoint *pep) +{ + /* Work around for stale data address in TRB. */ + if (pep->wa1_set) { + pep->wa1_set = 0; + pep->wa1_trb_index = 0xFFFF; + if (pep->wa1_cycle_bit) + pep->wa1_trb->control |= cpu_to_le32(0x1); + else + pep->wa1_trb->control &= cpu_to_le32(~0x1); + } +} + +static int cdns2_wa1_update_guard(struct cdns2_endpoint *pep, + struct cdns2_trb *trb) +{ + struct cdns2_device *pdev = pep->pdev; + + if (!pep->wa1_set) { + u32 doorbell; + + doorbell = !!(readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY); + + if (doorbell) { + pep->wa1_cycle_bit = pep->ring.pcs ? TRB_CYCLE : 0; + pep->wa1_set = 1; + pep->wa1_trb = trb; + pep->wa1_trb_index = pep->ring.enqueue; + return 0; + } + } + return 1; +} + +static void cdns2_wa1_tray_restore_cycle_bit(struct cdns2_device *pdev, + struct cdns2_endpoint *pep) +{ + int dma_index; + u32 doorbell; + + doorbell = !!(readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY); + dma_index = cdns2_get_dma_pos(pdev, pep); + + if (!doorbell || dma_index != pep->wa1_trb_index) + cdns2_wa1_restore_cycle_bit(pep); +} + +static int cdns2_prepare_ring(struct cdns2_device *pdev, + struct cdns2_endpoint *pep, + int num_trbs) +{ + struct cdns2_trb *link_trb = NULL; + int doorbell, dma_index; + struct cdns2_ring *ring; + u32 ch_bit = 0; + + ring = &pep->ring; + + if (num_trbs > ring->free_trbs) { + pep->ep_state |= EP_RING_FULL; + return -ENOBUFS; + } + + if ((ring->enqueue + num_trbs) >= (TRBS_PER_SEGMENT - 1)) { + doorbell = !!(readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY); + dma_index = cdns2_get_dma_pos(pdev, pep); + + /* Driver can't update LINK TRB if it is current processed. */ + if (doorbell && dma_index == TRBS_PER_SEGMENT - 1) { + pep->ep_state |= EP_DEFERRED_DRDY; + return -ENOBUFS; + } + + /* Update C bt in Link TRB before starting DMA. */ + link_trb = ring->trbs + (TRBS_PER_SEGMENT - 1); + + /* + * For TRs size equal 2 enabling TRB_CHAIN for epXin causes + * that DMA stuck at the LINK TRB. + * On the other hand, removing TRB_CHAIN for longer TRs for + * epXout cause that DMA stuck after handling LINK TRB. + * To eliminate this strange behavioral driver set TRB_CHAIN + * bit only for TR size > 2. + */ + if (pep->type == USB_ENDPOINT_XFER_ISOC || TRBS_PER_SEGMENT > 2) + ch_bit = TRB_CHAIN; + + link_trb->control = cpu_to_le32(((ring->pcs) ? TRB_CYCLE : 0) | + TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit); + } + + return 0; +} + +static void cdns2_dbg_request_trbs(struct cdns2_endpoint *pep, + struct cdns2_request *preq) +{ + struct cdns2_trb *link_trb = pep->ring.trbs + (TRBS_PER_SEGMENT - 1); + struct cdns2_trb *trb = preq->trb; + int num_trbs = preq->num_of_trb; + int i = 0; + + while (i < num_trbs) { + if (trb + i == link_trb) { + trb = pep->ring.trbs; + num_trbs = num_trbs - i; + i = 0; + } else { + i++; + } + } +} + +static unsigned int cdns2_count_trbs(struct cdns2_endpoint *pep, + u64 addr, u64 len) +{ + unsigned int num_trbs = 1; + + if (pep->type == USB_ENDPOINT_XFER_ISOC) { + /* + * To speed up DMA performance address should not exceed 4KB. + * for high bandwidth transfer and driver will split + * such buffer into two TRBs. + */ + num_trbs = DIV_ROUND_UP(len + + (addr & (TRB_MAX_ISO_BUFF_SIZE - 1)), + TRB_MAX_ISO_BUFF_SIZE); + + if (pep->interval > 1) + num_trbs = pep->dir ? num_trbs * pep->interval : 1; + } else if (pep->dir) { + /* + * One extra link trb for IN direction. + * Sometimes DMA doesn't want advance to next TD and transfer + * hangs. This extra Link TRB force DMA to advance to next TD. + */ + num_trbs++; + } + + return num_trbs; +} + +static unsigned int cdns2_count_sg_trbs(struct cdns2_endpoint *pep, + struct usb_request *req) +{ + unsigned int i, len, full_len, num_trbs = 0; + struct scatterlist *sg; + int trb_len = 0; + + full_len = req->length; + + for_each_sg(req->sg, sg, req->num_sgs, i) { + len = sg_dma_len(sg); + num_trbs += cdns2_count_trbs(pep, sg_dma_address(sg), len); + len = min(len, full_len); + + /* + * For HS ISO transfer TRBs should not exceed max packet size. + * When DMA is working, and data exceed max packet size then + * some data will be read in single mode instead burst mode. + * This behavior will drastically reduce the copying speed. + * To avoid this we need one or two extra TRBs. + * This issue occurs for UVC class with sg_supported = 1 + * because buffers addresses are not aligned to 1024. + */ + if (pep->type == USB_ENDPOINT_XFER_ISOC) { + u8 temp; + + trb_len += len; + temp = trb_len >> 10; + + if (temp) { + if (trb_len % 1024) + num_trbs = num_trbs + temp; + else + num_trbs = num_trbs + temp - 1; + + trb_len = trb_len - (temp << 10); + } + } + + full_len -= len; + if (full_len == 0) + break; + } + + return num_trbs; +} + +/* + * Function prepares the array with optimized AXI burst value for different + * transfer lengths. Controller handles the final data which are less + * then AXI burst size as single byte transactions. + * e.g.: + * Let's assume that driver prepares trb with trb->length 700 and burst size + * will be set to 128. In this case the controller will handle a first 512 as + * single AXI transaction but the next 188 bytes will be handled + * as 47 separate AXI transaction. + * The better solution is to use the burst size equal 16 and then we will + * have only 25 AXI transaction (10 * 64 + 15 *4). + */ +static void cdsn2_isoc_burst_opt(struct cdns2_device *pdev) +{ + int axi_burst_option[] = {1, 2, 4, 8, 16, 32, 64, 128}; + int best_burst; + int array_size; + int opt_burst; + int trb_size; + int i, j; + + array_size = ARRAY_SIZE(axi_burst_option); + + for (i = 0; i <= MAX_ISO_SIZE; i++) { + trb_size = i / 4; + best_burst = trb_size ? trb_size : 1; + + for (j = 0; j < array_size; j++) { + opt_burst = trb_size / axi_burst_option[j]; + opt_burst += trb_size % axi_burst_option[j]; + + if (opt_burst < best_burst) { + best_burst = opt_burst; + pdev->burst_opt[i] = axi_burst_option[j]; + } + } + } +} + +static void cdns2_ep_tx_isoc(struct cdns2_endpoint *pep, + struct cdns2_request *preq, + int num_trbs) +{ + struct scatterlist *sg = NULL; + u32 remaining_packet_size = 0; + struct cdns2_trb *trb; + bool first_trb = true; + dma_addr_t trb_dma; + u32 trb_buff_len; + u32 block_length; + int td_idx = 0; + int split_size; + u32 full_len; + int enqd_len; + int sent_len; + int sg_iter; + u32 control; + int num_tds; + u32 length; + + /* + * For OUT direction 1 TD per interval is enough + * because TRBs are not dumped by controller. + */ + num_tds = pep->dir ? pep->interval : 1; + split_size = preq->request.num_sgs ? 1024 : 3072; + + for (td_idx = 0; td_idx < num_tds; td_idx++) { + if (preq->request.num_sgs) { + sg = preq->request.sg; + trb_dma = sg_dma_address(sg); + block_length = sg_dma_len(sg); + } else { + trb_dma = preq->request.dma; + block_length = preq->request.length; + } + + full_len = preq->request.length; + sg_iter = preq->request.num_sgs ? preq->request.num_sgs : 1; + remaining_packet_size = split_size; + + for (enqd_len = 0; enqd_len < full_len; + enqd_len += trb_buff_len) { + if (remaining_packet_size == 0) + remaining_packet_size = split_size; + + /* + * Calculate TRB length.- buffer can't across 4KB + * and max packet size. + */ + trb_buff_len = TRB_BUFF_LEN_UP_TO_BOUNDARY(trb_dma); + trb_buff_len = min(trb_buff_len, remaining_packet_size); + trb_buff_len = min(trb_buff_len, block_length); + + if (trb_buff_len > full_len - enqd_len) + trb_buff_len = full_len - enqd_len; + + control = TRB_TYPE(TRB_NORMAL); + + /* + * For IN direction driver has to set the IOC for + * last TRB in last TD. + * For OUT direction driver must set IOC and ISP + * only for last TRB in each TDs. + */ + if (enqd_len + trb_buff_len >= full_len || !pep->dir) + control |= TRB_IOC | TRB_ISP; + + /* + * Don't give the first TRB to the hardware (by toggling + * the cycle bit) until we've finished creating all the + * other TRBs. + */ + if (first_trb) { + first_trb = false; + if (pep->ring.pcs == 0) + control |= TRB_CYCLE; + } else { + control |= pep->ring.pcs; + } + + if (enqd_len + trb_buff_len < full_len) + control |= TRB_CHAIN; + + length = TRB_LEN(trb_buff_len) | + TRB_BURST(pep->pdev->burst_opt[trb_buff_len]); + + trb = pep->ring.trbs + pep->ring.enqueue; + trb->buffer = cpu_to_le32(TRB_BUFFER(trb_dma)); + trb->length = cpu_to_le32(length); + trb->control = cpu_to_le32(control); + + trb_dma += trb_buff_len; + sent_len = trb_buff_len; + + if (sg && sent_len >= block_length) { + /* New sg entry */ + --sg_iter; + sent_len -= block_length; + if (sg_iter != 0) { + sg = sg_next(sg); + trb_dma = sg_dma_address(sg); + block_length = sg_dma_len(sg); + } + } + + remaining_packet_size -= trb_buff_len; + block_length -= sent_len; + preq->end_trb = pep->ring.enqueue; + + cdns2_ep_inc_enq(&pep->ring); + } + } +} + +static void cdns2_ep_tx_bulk(struct cdns2_endpoint *pep, + struct cdns2_request *preq, + int trbs_per_td) +{ + struct scatterlist *sg = NULL; + struct cdns2_ring *ring; + struct cdns2_trb *trb; + dma_addr_t trb_dma; + int sg_iter = 0; + u32 control; + u32 length; + + if (preq->request.num_sgs) { + sg = preq->request.sg; + trb_dma = sg_dma_address(sg); + length = sg_dma_len(sg); + } else { + trb_dma = preq->request.dma; + length = preq->request.length; + } + + ring = &pep->ring; + + for (sg_iter = 0; sg_iter < trbs_per_td; sg_iter++) { + control = TRB_TYPE(TRB_NORMAL) | ring->pcs | TRB_ISP; + trb = pep->ring.trbs + ring->enqueue; + + if (pep->dir && sg_iter == trbs_per_td - 1) { + preq->end_trb = ring->enqueue; + control = ring->pcs | TRB_TYPE(TRB_LINK) | TRB_CHAIN + | TRB_IOC; + cdns2_ep_inc_enq(&pep->ring); + + if (ring->enqueue == 0) + control |= TRB_TOGGLE; + + /* Point to next bad TRB. */ + trb->buffer = cpu_to_le32(pep->ring.dma + + (ring->enqueue * TRB_SIZE)); + trb->length = 0; + trb->control = cpu_to_le32(control); + break; + } + + /* + * Don't give the first TRB to the hardware (by toggling + * the cycle bit) until we've finished creating all the + * other TRBs. + */ + if (sg_iter == 0) + control = control ^ TRB_CYCLE; + + /* For last TRB in TD. */ + if (sg_iter == (trbs_per_td - (pep->dir ? 2 : 1))) + control |= TRB_IOC; + else + control |= TRB_CHAIN; + + trb->buffer = cpu_to_le32(trb_dma); + trb->length = cpu_to_le32(TRB_BURST(pep->trb_burst_size) | + TRB_LEN(length)); + trb->control = cpu_to_le32(control); + + if (sg && sg_iter < (trbs_per_td - 1)) { + sg = sg_next(sg); + trb_dma = sg_dma_address(sg); + length = sg_dma_len(sg); + } + + preq->end_trb = ring->enqueue; + cdns2_ep_inc_enq(&pep->ring); + } +} + +static void cdns2_set_drdy(struct cdns2_device *pdev, + struct cdns2_endpoint *pep) +{ + /* + * Memory barrier - Cycle Bit must be set before doorbell. + */ + dma_wmb(); + + /* Clearing TRBERR and DESCMIS before setting DRDY. */ + writel(DMA_EP_STS_TRBERR | DMA_EP_STS_DESCMIS, + &pdev->adma_regs->ep_sts); + writel(DMA_EP_CMD_DRDY, &pdev->adma_regs->ep_cmd); + + if (readl(&pdev->adma_regs->ep_sts) & DMA_EP_STS_TRBERR) { + writel(DMA_EP_STS_TRBERR, &pdev->adma_regs->ep_sts); + writel(DMA_EP_CMD_DRDY, &pdev->adma_regs->ep_cmd); + } +} + +static int cdns2_prepare_first_isoc_transfer(struct cdns2_device *pdev, + struct cdns2_endpoint *pep) +{ + struct cdns2_trb *trb; + u32 buffer; + u8 hw_ccs; + + if ((readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY)) + return -EBUSY; + + if (!pep->dir) { + set_reg_bit_32(&pdev->adma_regs->ep_cfg, DMA_EP_CFG_ENABLE); + writel(pep->ring.dma + pep->ring.dequeue, + &pdev->adma_regs->ep_traddr); + return 0; + } + + /* + * The first packet after doorbell can be corrupted so, + * driver prepares 0 length packet as first packet. + */ + buffer = pep->ring.dma + pep->ring.dequeue * TRB_SIZE; + hw_ccs = !!DMA_EP_STS_CCS(readl(&pdev->adma_regs->ep_sts)); + + trb = &pep->ring.trbs[TRBS_PER_SEGMENT]; + trb->length = 0; + trb->buffer = cpu_to_le32(TRB_BUFFER(buffer)); + trb->control = cpu_to_le32((hw_ccs ? TRB_CYCLE : 0) | TRB_TYPE(TRB_NORMAL)); + + /* + * LINK TRB is used to force updating cycle bit in controller and + * move to correct place in transfer ring. + */ + trb++; + trb->length = 0; + trb->buffer = cpu_to_le32(TRB_BUFFER(buffer)); + trb->control = cpu_to_le32((hw_ccs ? TRB_CYCLE : 0) | + TRB_TYPE(TRB_LINK) | TRB_CHAIN); + + if (hw_ccs != pep->ring.ccs) + trb->control |= cpu_to_le32(TRB_TOGGLE); + + set_reg_bit_32(&pdev->adma_regs->ep_cfg, DMA_EP_CFG_ENABLE); + writel(pep->ring.dma + (TRBS_PER_SEGMENT * TRB_SIZE), + &pdev->adma_regs->ep_traddr); + + return 0; +} + +/* Prepare and start transfer on no-default endpoint. */ +static int cdns2_ep_run_transfer(struct cdns2_endpoint *pep, + struct cdns2_request *preq) +{ + struct cdns2_device *pdev = pep->pdev; + struct cdns2_ring *ring; + u32 togle_pcs = 1; + int num_trbs; + int ret; + + cdns2_select_ep(pdev, pep->endpoint.address); + + if (preq->request.sg) + num_trbs = cdns2_count_sg_trbs(pep, &preq->request); + else + num_trbs = cdns2_count_trbs(pep, preq->request.dma, + preq->request.length); + + ret = cdns2_prepare_ring(pdev, pep, num_trbs); + if (ret) + return ret; + + ring = &pep->ring; + preq->start_trb = ring->enqueue; + preq->trb = ring->trbs + ring->enqueue; + + if (usb_endpoint_xfer_isoc(pep->endpoint.desc)) { + cdns2_ep_tx_isoc(pep, preq, num_trbs); + } else { + togle_pcs = cdns2_wa1_update_guard(pep, ring->trbs + ring->enqueue); + cdns2_ep_tx_bulk(pep, preq, num_trbs); + } + + preq->num_of_trb = num_trbs; + + /* + * Memory barrier - cycle bit must be set as the last operation. + */ + dma_wmb(); + + /* Give the TD to the consumer. */ + if (togle_pcs) + preq->trb->control = preq->trb->control ^ cpu_to_le32(1); + + cdns2_wa1_tray_restore_cycle_bit(pdev, pep); + cdns2_dbg_request_trbs(pep, preq); + + if (!pep->wa1_set && !(pep->ep_state & EP_STALLED) && !pep->skip) { + if (pep->type == USB_ENDPOINT_XFER_ISOC) { + ret = cdns2_prepare_first_isoc_transfer(pdev, pep); + if (ret) + return 0; + } + + cdns2_set_drdy(pdev, pep); + } + + return 0; +} + +/* Prepare and start transfer for all not started requests. */ +static int cdns2_start_all_request(struct cdns2_device *pdev, + struct cdns2_endpoint *pep) +{ + struct cdns2_request *preq; + int ret; + + while (!list_empty(&pep->deferred_list)) { + preq = cdns2_next_preq(&pep->deferred_list); + + ret = cdns2_ep_run_transfer(pep, preq); + if (ret) + return ret; + + list_move_tail(&preq->list, &pep->pending_list); + } + + pep->ep_state &= ~EP_RING_FULL; + + return 0; +} + +/* + * Check whether trb has been handled by DMA. + * + * Endpoint must be selected before invoking this function. + * + * Returns false if request has not been handled by DMA, else returns true. + * + * SR - start ring + * ER - end ring + * DQ = ring->dequeue - dequeue position + * EQ = ring->enqueue - enqueue position + * ST = preq->start_trb - index of first TRB in transfer ring + * ET = preq->end_trb - index of last TRB in transfer ring + * CI = current_index - index of processed TRB by DMA. + * + * As first step, we check if the TRB between the ST and ET. + * Then, we check if cycle bit for index pep->dequeue + * is correct. + * + * some rules: + * 1. ring->dequeue never equals to current_index. + * 2 ring->enqueue never exceed ring->dequeue + * 3. exception: ring->enqueue == ring->dequeue + * and ring->free_trbs is zero. + * This case indicate that TR is full. + * + * At below two cases, the request have been handled. + * Case 1 - ring->dequeue < current_index + * SR ... EQ ... DQ ... CI ... ER + * SR ... DQ ... CI ... EQ ... ER + * + * Case 2 - ring->dequeue > current_index + * This situation takes place when CI go through the LINK TRB at the end of + * transfer ring. + * SR ... CI ... EQ ... DQ ... ER + */ +static bool cdns2_trb_handled(struct cdns2_endpoint *pep, + struct cdns2_request *preq) +{ + struct cdns2_device *pdev = pep->pdev; + struct cdns2_ring *ring; + struct cdns2_trb *trb; + int current_index = 0; + int handled = 0; + int doorbell; + + ring = &pep->ring; + current_index = cdns2_get_dma_pos(pdev, pep); + doorbell = !!(readl(&pdev->adma_regs->ep_cmd) & DMA_EP_CMD_DRDY); + + /* + * Only ISO transfer can use 2 entries outside the standard + * Transfer Ring. First of them is used as zero length packet and the + * second as LINK TRB. + */ + if (current_index >= TRBS_PER_SEGMENT) + goto finish; + + /* Current trb doesn't belong to this request. */ + if (preq->start_trb < preq->end_trb) { + if (ring->dequeue > preq->end_trb) + goto finish; + + if (ring->dequeue < preq->start_trb) + goto finish; + } + + if (preq->start_trb > preq->end_trb && ring->dequeue > preq->end_trb && + ring->dequeue < preq->start_trb) + goto finish; + + if (preq->start_trb == preq->end_trb && ring->dequeue != preq->end_trb) + goto finish; + + trb = &ring->trbs[ring->dequeue]; + + if ((le32_to_cpu(trb->control) & TRB_CYCLE) != ring->ccs) + goto finish; + + if (doorbell == 1 && current_index == ring->dequeue) + goto finish; + + /* The corner case for TRBS_PER_SEGMENT equal 2). */ + if (TRBS_PER_SEGMENT == 2 && pep->type != USB_ENDPOINT_XFER_ISOC) { + handled = 1; + goto finish; + } + + if (ring->enqueue == ring->dequeue && + ring->free_trbs == 0) { + handled = 1; + } else if (ring->dequeue < current_index) { + if ((current_index == (TRBS_PER_SEGMENT - 1)) && + !ring->dequeue) + goto finish; + + handled = 1; + } else if (ring->dequeue > current_index) { + handled = 1; + } + +finish: + + return handled; +} + +static void cdns2_skip_isoc_td(struct cdns2_device *pdev, + struct cdns2_endpoint *pep, + struct cdns2_request *preq) +{ + struct cdns2_trb *trb; + int i; + + trb = pep->ring.trbs + pep->ring.dequeue; + + for (i = preq->finished_trb ; i < preq->num_of_trb; i++) { + preq->finished_trb++; + cdns2_ep_inc_deq(&pep->ring); + trb = cdns2_next_trb(pep, trb); + } + + cdns2_gadget_giveback(pep, preq, 0); + cdns2_prepare_first_isoc_transfer(pdev, pep); + pep->skip = false; + cdns2_set_drdy(pdev, pep); +} + +static void cdns2_transfer_completed(struct cdns2_device *pdev, + struct cdns2_endpoint *pep) +{ + struct cdns2_request *preq = NULL; + bool request_handled = false; + struct cdns2_trb *trb; + + while (!list_empty(&pep->pending_list)) { + preq = cdns2_next_preq(&pep->pending_list); + trb = pep->ring.trbs + pep->ring.dequeue; + + /* + * The TRB was changed as link TRB, and the request + * was handled at ep_dequeue. + */ + while (TRB_FIELD_TO_TYPE(le32_to_cpu(trb->control)) == TRB_LINK && + le32_to_cpu(trb->length)) { + cdns2_ep_inc_deq(&pep->ring); + trb = pep->ring.trbs + pep->ring.dequeue; + } + + /* + * Re-select endpoint. It could be changed by other CPU + * during handling usb_gadget_giveback_request. + */ + cdns2_select_ep(pdev, pep->endpoint.address); + + while (cdns2_trb_handled(pep, preq)) { + preq->finished_trb++; + + if (preq->finished_trb >= preq->num_of_trb) + request_handled = true; + + trb = pep->ring.trbs + pep->ring.dequeue; + + if (pep->dir && pep->type == USB_ENDPOINT_XFER_ISOC) + /* + * For ISOC IN controller doens't update the + * trb->length. + */ + preq->request.actual = preq->request.length; + else + preq->request.actual += + TRB_LEN(le32_to_cpu(trb->length)); + + cdns2_ep_inc_deq(&pep->ring); + } + + if (request_handled) { + cdns2_gadget_giveback(pep, preq, 0); + request_handled = false; + } else { + goto prepare_next_td; + } + + if (pep->type != USB_ENDPOINT_XFER_ISOC && + TRBS_PER_SEGMENT == 2) + break; + } + +prepare_next_td: + if (pep->skip && preq) + cdns2_skip_isoc_td(pdev, pep, preq); + + if (!(pep->ep_state & EP_STALLED) && + !(pep->ep_state & EP_STALL_PENDING)) + cdns2_start_all_request(pdev, pep); +} + +static void cdns2_wakeup(struct cdns2_device *pdev) +{ + if (!pdev->may_wakeup) + return; + + /* Start driving resume signaling to indicate remote wakeup. */ + set_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_SIGRSUME); +} + +static void cdns2_rearm_transfer(struct cdns2_endpoint *pep, u8 rearm) +{ + struct cdns2_device *pdev = pep->pdev; + + cdns2_wa1_restore_cycle_bit(pep); + + if (rearm) { + /* Cycle Bit must be updated before arming DMA. */ + dma_wmb(); + + writel(DMA_EP_CMD_DRDY, &pdev->adma_regs->ep_cmd); + + cdns2_wakeup(pdev); + } +} + +static void cdns2_handle_epx_interrupt(struct cdns2_endpoint *pep) +{ + struct cdns2_device *pdev = pep->pdev; + u8 isoerror = 0; + u32 ep_sts_reg; + u32 val; + + cdns2_select_ep(pdev, pep->endpoint.address); + + ep_sts_reg = readl(&pdev->adma_regs->ep_sts); + writel(ep_sts_reg, &pdev->adma_regs->ep_sts); + + if (pep->type == USB_ENDPOINT_XFER_ISOC) { + u8 mult; + u8 cs; + + mult = USB_EP_MAXP_MULT(pep->endpoint.desc->wMaxPacketSize); + cs = pep->dir ? readb(&pdev->epx_regs->ep[pep->num - 1].txcs) : + readb(&pdev->epx_regs->ep[pep->num - 1].rxcs); + if (mult > 0) + isoerror = EPX_CS_ERR(cs); + } + + /* + * Sometimes ISO Error for mult=1 or mult=2 is not propagated on time + * from USB module to DMA module. To protect against this driver + * checks also the txcs/rxcs registers. + */ + if ((ep_sts_reg & DMA_EP_STS_ISOERR) || isoerror) { + clear_reg_bit_32(&pdev->adma_regs->ep_cfg, DMA_EP_CFG_ENABLE); + + /* Wait for DBUSY cleared. */ + readl_poll_timeout_atomic(&pdev->adma_regs->ep_sts, val, + !(val & DMA_EP_STS_DBUSY), 1, 125); + + writel(DMA_EP_CMD_DFLUSH, &pep->pdev->adma_regs->ep_cmd); + + /* Wait for DFLUSH cleared. */ + readl_poll_timeout_atomic(&pep->pdev->adma_regs->ep_cmd, val, + !(val & DMA_EP_CMD_DFLUSH), 1, 10); + + pep->skip = true; + } + + if (ep_sts_reg & DMA_EP_STS_TRBERR || pep->skip) { + if (pep->ep_state & EP_STALL_PENDING && + !(ep_sts_reg & DMA_EP_STS_DESCMIS)) + cdns2_ep_stall_flush(pep); + + /* + * For isochronous transfer driver completes request on + * IOC or on TRBERR. IOC appears only when device receive + * OUT data packet. If host disable stream or lost some packet + * then the only way to finish all queued transfer is to do it + * on TRBERR event. + */ + if (pep->type == USB_ENDPOINT_XFER_ISOC && !pep->wa1_set) { + if (!pep->dir) + clear_reg_bit_32(&pdev->adma_regs->ep_cfg, + DMA_EP_CFG_ENABLE); + + cdns2_transfer_completed(pdev, pep); + if (pep->ep_state & EP_DEFERRED_DRDY) { + pep->ep_state &= ~EP_DEFERRED_DRDY; + cdns2_set_drdy(pdev, pep); + } + + return; + } + + cdns2_transfer_completed(pdev, pep); + + if (!(pep->ep_state & EP_STALLED) && + !(pep->ep_state & EP_STALL_PENDING)) { + if (pep->ep_state & EP_DEFERRED_DRDY) { + pep->ep_state &= ~EP_DEFERRED_DRDY; + cdns2_start_all_request(pdev, pep); + } else { + cdns2_rearm_transfer(pep, pep->wa1_set); + } + } + + return; + } + + if ((ep_sts_reg & DMA_EP_STS_IOC) || (ep_sts_reg & DMA_EP_STS_ISP)) + cdns2_transfer_completed(pdev, pep); +} + +static void cdns2_disconnect_gadget(struct cdns2_device *pdev) +{ + if (pdev->gadget_driver && pdev->gadget_driver->disconnect) + pdev->gadget_driver->disconnect(&pdev->gadget); +} + +static irqreturn_t cdns2_usb_irq_handler(int irq, void *data) +{ + struct cdns2_device *pdev = data; + unsigned long reg_ep_ists; + u8 reg_usb_irq_m; + u8 reg_ext_irq_m; + u8 reg_usb_irq; + u8 reg_ext_irq; + + if (pdev->in_lpm) + return IRQ_NONE; + + reg_usb_irq_m = readb(&pdev->interrupt_regs->usbien); + reg_ext_irq_m = readb(&pdev->interrupt_regs->extien); + + /* Mask all sources of interrupt. */ + writeb(0, &pdev->interrupt_regs->usbien); + writeb(0, &pdev->interrupt_regs->extien); + writel(0, &pdev->adma_regs->ep_ien); + + /* Clear interrupt sources. */ + writel(0, &pdev->adma_regs->ep_sts); + writeb(0, &pdev->interrupt_regs->usbirq); + writeb(0, &pdev->interrupt_regs->extirq); + + reg_ep_ists = readl(&pdev->adma_regs->ep_ists); + reg_usb_irq = readb(&pdev->interrupt_regs->usbirq); + reg_ext_irq = readb(&pdev->interrupt_regs->extirq); + + if (reg_ep_ists || (reg_usb_irq & reg_usb_irq_m) || + (reg_ext_irq & reg_ext_irq_m)) + return IRQ_WAKE_THREAD; + + writeb(USB_IEN_INIT, &pdev->interrupt_regs->usbien); + writeb(EXTIRQ_WAKEUP, &pdev->interrupt_regs->extien); + writel(~0, &pdev->adma_regs->ep_ien); + + return IRQ_NONE; +} + +static irqreturn_t cdns2_thread_usb_irq_handler(struct cdns2_device *pdev) +{ + u8 usb_irq, ext_irq; + int speed; + int i; + + ext_irq = readb(&pdev->interrupt_regs->extirq) & EXTIRQ_WAKEUP; + writeb(ext_irq, &pdev->interrupt_regs->extirq); + + usb_irq = readb(&pdev->interrupt_regs->usbirq) & USB_IEN_INIT; + writeb(usb_irq, &pdev->interrupt_regs->usbirq); + + if (!ext_irq && !usb_irq) + return IRQ_NONE; + + if (ext_irq & EXTIRQ_WAKEUP) { + if (pdev->gadget_driver && pdev->gadget_driver->resume) { + spin_unlock(&pdev->lock); + pdev->gadget_driver->resume(&pdev->gadget); + spin_lock(&pdev->lock); + } + } + + if (usb_irq & USBIRQ_LPM) { + u8 reg = readb(&pdev->usb_regs->lpmctrl); + + /* LPM1 enter */ + if (!(reg & LPMCTRLLH_LPMNYET)) + writeb(0, &pdev->usb_regs->sleep_clkgate); + } + + if (usb_irq & USBIRQ_SUSPEND) { + if (pdev->gadget_driver && pdev->gadget_driver->suspend) { + spin_unlock(&pdev->lock); + pdev->gadget_driver->suspend(&pdev->gadget); + spin_lock(&pdev->lock); + } + } + + if (usb_irq & USBIRQ_URESET) { + if (pdev->gadget_driver) { + pdev->dev_address = 0; + + spin_unlock(&pdev->lock); + usb_gadget_udc_reset(&pdev->gadget, + pdev->gadget_driver); + spin_lock(&pdev->lock); + + /* + * The USBIRQ_URESET is reported at the beginning of + * reset signal. 100ms is enough time to finish reset + * process. For high-speed reset procedure is completed + * when controller detect HS mode. + */ + for (i = 0; i < 100; i++) { + mdelay(1); + speed = cdns2_get_speed(pdev); + if (speed == USB_SPEED_HIGH) + break; + } + + pdev->gadget.speed = speed; + cdns2_enable_l1(pdev, 0); + cdns2_ep0_config(pdev); + pdev->may_wakeup = 0; + } + } + + if (usb_irq & USBIRQ_SUDAV) { + pdev->ep0_stage = CDNS2_SETUP_STAGE; + cdns2_handle_setup_packet(pdev); + } + + return IRQ_HANDLED; +} + +/* Deferred USB interrupt handler. */ +static irqreturn_t cdns2_thread_irq_handler(int irq, void *data) +{ + struct cdns2_device *pdev = data; + unsigned long dma_ep_ists; + unsigned long flags; + unsigned int bit; + + local_bh_disable(); + spin_lock_irqsave(&pdev->lock, flags); + + cdns2_thread_usb_irq_handler(pdev); + + dma_ep_ists = readl(&pdev->adma_regs->ep_ists); + if (!dma_ep_ists) + goto unlock; + + /* Handle default endpoint OUT. */ + if (dma_ep_ists & DMA_EP_ISTS_EP_OUT0) + cdns2_handle_ep0_interrupt(pdev, USB_DIR_OUT); + + /* Handle default endpoint IN. */ + if (dma_ep_ists & DMA_EP_ISTS_EP_IN0) + cdns2_handle_ep0_interrupt(pdev, USB_DIR_IN); + + dma_ep_ists &= ~(DMA_EP_ISTS_EP_OUT0 | DMA_EP_ISTS_EP_IN0); + + for_each_set_bit(bit, &dma_ep_ists, sizeof(u32) * BITS_PER_BYTE) { + u8 ep_idx = bit > 16 ? (bit - 16) * 2 : (bit * 2) - 1; + + /* + * Endpoints in pdev->eps[] are held in order: + * ep0, ep1out, ep1in, ep2out, ep2in... ep15out, ep15in. + * but in dma_ep_ists in order: + * ep0 ep1out ep2out ... ep15out ep0in ep1in .. ep15in + */ + cdns2_handle_epx_interrupt(&pdev->eps[ep_idx]); + } + +unlock: + writel(~0, &pdev->adma_regs->ep_ien); + writeb(USB_IEN_INIT, &pdev->interrupt_regs->usbien); + writeb(EXTIRQ_WAKEUP, &pdev->interrupt_regs->extien); + + spin_unlock_irqrestore(&pdev->lock, flags); + local_bh_enable(); + + return IRQ_HANDLED; +} + +/* Calculates and assigns onchip memory for endpoints. */ +static void cdns2_eps_onchip_buffer_init(struct cdns2_device *pdev) +{ + struct cdns2_endpoint *pep; + int min_buf_tx = 0; + int min_buf_rx = 0; + u16 tx_offset = 0; + u16 rx_offset = 0; + int free; + int i; + + for (i = 0; i < CDNS2_ENDPOINTS_NUM; i++) { + pep = &pdev->eps[i]; + + if (!(pep->ep_state & EP_CLAIMED)) + continue; + + if (pep->dir) + min_buf_tx += pep->buffering; + else + min_buf_rx += pep->buffering; + } + + for (i = 0; i < CDNS2_ENDPOINTS_NUM; i++) { + pep = &pdev->eps[i]; + + if (!(pep->ep_state & EP_CLAIMED)) + continue; + + if (pep->dir) { + free = pdev->onchip_tx_buf - min_buf_tx; + + if (free + pep->buffering >= 4) + free = 4; + else + free = free + pep->buffering; + + min_buf_tx = min_buf_tx - pep->buffering + free; + + pep->buffering = free; + + writel(tx_offset, + &pdev->epx_regs->txstaddr[pep->num - 1]); + pdev->epx_regs->txstaddr[pep->num - 1] = tx_offset; + + dev_dbg(pdev->dev, "%s onchip address %04x, buffering: %d\n", + pep->name, tx_offset, pep->buffering); + + tx_offset += pep->buffering * 1024; + } else { + free = pdev->onchip_rx_buf - min_buf_rx; + + if (free + pep->buffering >= 4) + free = 4; + else + free = free + pep->buffering; + + min_buf_rx = min_buf_rx - pep->buffering + free; + + pep->buffering = free; + writel(rx_offset, + &pdev->epx_regs->rxstaddr[pep->num - 1]); + + dev_dbg(pdev->dev, "%s onchip address %04x, buffering: %d\n", + pep->name, rx_offset, pep->buffering); + + rx_offset += pep->buffering * 1024; + } + } +} + +/* Configure hardware endpoint. */ +static int cdns2_ep_config(struct cdns2_endpoint *pep, bool enable) +{ + bool is_iso_ep = (pep->type == USB_ENDPOINT_XFER_ISOC); + struct cdns2_device *pdev = pep->pdev; + u32 max_packet_size; + u8 dir = 0; + u8 ep_cfg; + u8 mult; + u32 val; + int ret; + + switch (pep->type) { + case USB_ENDPOINT_XFER_INT: + ep_cfg = EPX_CON_TYPE_INT; + break; + case USB_ENDPOINT_XFER_BULK: + ep_cfg = EPX_CON_TYPE_BULK; + break; + default: + mult = USB_EP_MAXP_MULT(pep->endpoint.desc->wMaxPacketSize); + ep_cfg = mult << EPX_CON_ISOD_SHIFT; + ep_cfg |= EPX_CON_TYPE_ISOC; + + if (pep->dir) { + set_reg_bit_8(&pdev->epx_regs->isoautoarm, BIT(pep->num)); + set_reg_bit_8(&pdev->epx_regs->isoautodump, BIT(pep->num)); + set_reg_bit_8(&pdev->epx_regs->isodctrl, BIT(pep->num)); + } + } + + switch (pdev->gadget.speed) { + case USB_SPEED_FULL: + max_packet_size = is_iso_ep ? 1023 : 64; + break; + case USB_SPEED_HIGH: + max_packet_size = is_iso_ep ? 1024 : 512; + break; + default: + /* All other speed are not supported. */ + return -EINVAL; + } + + ep_cfg |= (EPX_CON_VAL | (pep->buffering - 1)); + + if (pep->dir) { + dir = FIFOCTRL_IO_TX; + writew(max_packet_size, &pdev->epx_regs->txmaxpack[pep->num - 1]); + writeb(ep_cfg, &pdev->epx_regs->ep[pep->num - 1].txcon); + } else { + writew(max_packet_size, &pdev->epx_regs->rxmaxpack[pep->num - 1]); + writeb(ep_cfg, &pdev->epx_regs->ep[pep->num - 1].rxcon); + } + + writeb(pep->num | dir | FIFOCTRL_FIFOAUTO, + &pdev->usb_regs->fifoctrl); + writeb(pep->num | dir, &pdev->epx_regs->endprst); + writeb(pep->num | ENDPRST_FIFORST | ENDPRST_TOGRST | dir, + &pdev->epx_regs->endprst); + + if (max_packet_size == 1024) + pep->trb_burst_size = 128; + else if (max_packet_size >= 512) + pep->trb_burst_size = 64; + else + pep->trb_burst_size = 16; + + cdns2_select_ep(pdev, pep->num | pep->dir); + writel(DMA_EP_CMD_EPRST | DMA_EP_CMD_DFLUSH, &pdev->adma_regs->ep_cmd); + + ret = readl_poll_timeout_atomic(&pdev->adma_regs->ep_cmd, val, + !(val & (DMA_EP_CMD_DFLUSH | + DMA_EP_CMD_EPRST)), + 1, 1000); + + if (ret) + return ret; + + writel(DMA_EP_STS_TRBERR | DMA_EP_STS_ISOERR, &pdev->adma_regs->ep_sts_en); + + if (enable) + writel(DMA_EP_CFG_ENABLE, &pdev->adma_regs->ep_cfg); + + dev_dbg(pdev->dev, "Configure %s: with MPS: %08x, ep con: %02x\n", + pep->name, max_packet_size, ep_cfg); + + return 0; +} + +struct usb_request *cdns2_gadget_ep_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags) +{ + struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep); + struct cdns2_request *preq; + + preq = kzalloc(sizeof(*preq), gfp_flags); + if (!preq) + return NULL; + + preq->pep = pep; + + return &preq->request; +} + +void cdns2_gadget_ep_free_request(struct usb_ep *ep, + struct usb_request *request) +{ + struct cdns2_request *preq = to_cdns2_request(request); + + kfree(preq); +} + +static int cdns2_gadget_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + u32 reg = DMA_EP_STS_EN_TRBERREN; + struct cdns2_endpoint *pep; + struct cdns2_device *pdev; + unsigned long flags; + int enable = 1; + int ret = 0; + + if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT || + !desc->wMaxPacketSize) { + return -EINVAL; + } + + pep = ep_to_cdns2_ep(ep); + pdev = pep->pdev; + + if (dev_WARN_ONCE(pdev->dev, pep->ep_state & EP_ENABLED, + "%s is already enabled\n", pep->name)) + return 0; + + spin_lock_irqsave(&pdev->lock, flags); + + pep->type = usb_endpoint_type(desc); + pep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0; + + if (pdev->gadget.speed == USB_SPEED_FULL) + if (pep->type == USB_ENDPOINT_XFER_INT) + pep->interval = desc->bInterval; + + if (pep->interval > ISO_MAX_INTERVAL && + pep->type == USB_ENDPOINT_XFER_ISOC) { + dev_err(pdev->dev, "ISO period is limited to %d (current: %d)\n", + ISO_MAX_INTERVAL, pep->interval); + + ret = -EINVAL; + goto exit; + } + + /* + * During ISO OUT traffic DMA reads Transfer Ring for the EP which has + * never got doorbell. + * This issue was detected only on simulation, but to avoid this issue + * driver add protection against it. To fix it driver enable ISO OUT + * endpoint before setting DRBL. This special treatment of ISO OUT + * endpoints are recommended by controller specification. + */ + if (pep->type == USB_ENDPOINT_XFER_ISOC && !pep->dir) + enable = 0; + + ret = cdns2_alloc_tr_segment(pep); + if (ret) + goto exit; + + ret = cdns2_ep_config(pep, enable); + if (ret) { + cdns2_free_tr_segment(pep); + ret = -EINVAL; + goto exit; + } + + pep->ep_state &= ~(EP_STALLED | EP_STALL_PENDING); + pep->ep_state |= EP_ENABLED; + pep->wa1_set = 0; + pep->ring.enqueue = 0; + pep->ring.dequeue = 0; + reg = readl(&pdev->adma_regs->ep_sts); + pep->ring.pcs = !!DMA_EP_STS_CCS(reg); + pep->ring.ccs = !!DMA_EP_STS_CCS(reg); + + writel(pep->ring.dma, &pdev->adma_regs->ep_traddr); + + /* one TRB is reserved for link TRB used in DMULT mode*/ + pep->ring.free_trbs = TRBS_PER_SEGMENT - 1; + +exit: + spin_unlock_irqrestore(&pdev->lock, flags); + + return ret; +} + +static int cdns2_gadget_ep_disable(struct usb_ep *ep) +{ + struct cdns2_endpoint *pep; + struct cdns2_request *preq; + struct cdns2_device *pdev; + unsigned long flags; + int val; + + if (!ep) + return -EINVAL; + + pep = ep_to_cdns2_ep(ep); + pdev = pep->pdev; + + if (dev_WARN_ONCE(pdev->dev, !(pep->ep_state & EP_ENABLED), + "%s is already disabled\n", pep->name)) + return 0; + + spin_lock_irqsave(&pdev->lock, flags); + + cdns2_select_ep(pdev, ep->desc->bEndpointAddress); + + clear_reg_bit_32(&pdev->adma_regs->ep_cfg, DMA_EP_CFG_ENABLE); + + /* + * Driver needs some time before resetting endpoint. + * It need waits for clearing DBUSY bit or for timeout expired. + * 10us is enough time for controller to stop transfer. + */ + readl_poll_timeout_atomic(&pdev->adma_regs->ep_sts, val, + !(val & DMA_EP_STS_DBUSY), 1, 10); + writel(DMA_EP_CMD_EPRST, &pdev->adma_regs->ep_cmd); + + readl_poll_timeout_atomic(&pdev->adma_regs->ep_cmd, val, + !(val & (DMA_EP_CMD_DFLUSH | DMA_EP_CMD_EPRST)), + 1, 1000); + + while (!list_empty(&pep->pending_list)) { + preq = cdns2_next_preq(&pep->pending_list); + cdns2_gadget_giveback(pep, preq, -ESHUTDOWN); + } + + while (!list_empty(&pep->deferred_list)) { + preq = cdns2_next_preq(&pep->deferred_list); + cdns2_gadget_giveback(pep, preq, -ESHUTDOWN); + } + + ep->desc = NULL; + pep->ep_state &= ~EP_ENABLED; + + spin_unlock_irqrestore(&pdev->lock, flags); + + return 0; +} + +static int cdns2_ep_enqueue(struct cdns2_endpoint *pep, + struct cdns2_request *preq, + gfp_t gfp_flags) +{ + struct cdns2_device *pdev = pep->pdev; + struct usb_request *request; + int ret; + + request = &preq->request; + request->actual = 0; + request->status = -EINPROGRESS; + + ret = usb_gadget_map_request_by_dev(pdev->dev, request, pep->dir); + if (ret) + return ret; + + list_add_tail(&preq->list, &pep->deferred_list); + + if (!(pep->ep_state & EP_STALLED) && !(pep->ep_state & EP_STALL_PENDING)) + cdns2_start_all_request(pdev, pep); + + return 0; +} + +static int cdns2_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, + gfp_t gfp_flags) +{ + struct usb_request *zlp_request; + struct cdns2_request *preq; + struct cdns2_endpoint *pep; + struct cdns2_device *pdev; + unsigned long flags; + int ret; + + if (!request || !ep) + return -EINVAL; + + pep = ep_to_cdns2_ep(ep); + pdev = pep->pdev; + + if (!(pep->ep_state & EP_ENABLED)) { + dev_err(pdev->dev, "%s: can't queue to disabled endpoint\n", + pep->name); + return -EINVAL; + } + + spin_lock_irqsave(&pdev->lock, flags); + + preq = to_cdns2_request(request); + ret = cdns2_ep_enqueue(pep, preq, gfp_flags); + + if (ret == 0 && request->zero && request->length && + (request->length % ep->maxpacket == 0)) { + struct cdns2_request *preq; + + zlp_request = cdns2_gadget_ep_alloc_request(ep, GFP_ATOMIC); + zlp_request->buf = pdev->zlp_buf; + zlp_request->length = 0; + + preq = to_cdns2_request(zlp_request); + ret = cdns2_ep_enqueue(pep, preq, gfp_flags); + } + + spin_unlock_irqrestore(&pdev->lock, flags); + return ret; +} + +int cdns2_gadget_ep_dequeue(struct usb_ep *ep, + struct usb_request *request) +{ + struct cdns2_request *preq, *preq_temp, *cur_preq; + struct cdns2_endpoint *pep; + struct cdns2_trb *link_trb; + u8 req_on_hw_ring = 0; + unsigned long flags; + u32 buffer; + int val, i; + + if (!ep || !request || !ep->desc) + return -EINVAL; + + pep = ep_to_cdns2_ep(ep); + if (!pep->endpoint.desc) { + dev_err(pep->pdev->dev, "%s: can't dequeue to disabled endpoint\n", + pep->name); + return -ESHUTDOWN; + } + + /* Requests has been dequeued during disabling endpoint. */ + if (!(pep->ep_state & EP_ENABLED)) + return 0; + + spin_lock_irqsave(&pep->pdev->lock, flags); + + cur_preq = to_cdns2_request(request); + + list_for_each_entry_safe(preq, preq_temp, &pep->pending_list, list) { + if (cur_preq == preq) { + req_on_hw_ring = 1; + goto found; + } + } + + list_for_each_entry_safe(preq, preq_temp, &pep->deferred_list, list) { + if (cur_preq == preq) + goto found; + } + + goto not_found; + +found: + link_trb = preq->trb; + + /* Update ring only if removed request is on pending_req_list list. */ + if (req_on_hw_ring && link_trb) { + /* Stop DMA */ + writel(DMA_EP_CMD_DFLUSH, &pep->pdev->adma_regs->ep_cmd); + + /* Wait for DFLUSH cleared. */ + readl_poll_timeout_atomic(&pep->pdev->adma_regs->ep_cmd, val, + !(val & DMA_EP_CMD_DFLUSH), 1, 1000); + + buffer = cpu_to_le32(TRB_BUFFER(pep->ring.dma + + ((preq->end_trb + 1) * TRB_SIZE))); + + for (i = 0; i < preq->num_of_trb; i++) { + link_trb->buffer = buffer; + link_trb->control = cpu_to_le32((le32_to_cpu(link_trb->control) + & TRB_CYCLE) | TRB_CHAIN | + TRB_TYPE(TRB_LINK)); + + link_trb = cdns2_next_trb(pep, link_trb); + } + + if (pep->wa1_trb == preq->trb) + cdns2_wa1_restore_cycle_bit(pep); + } + + cdns2_gadget_giveback(pep, cur_preq, -ECONNRESET); + + preq = cdns2_next_preq(&pep->pending_list); + if (preq) + cdns2_rearm_transfer(pep, 1); + +not_found: + spin_unlock_irqrestore(&pep->pdev->lock, flags); + return 0; +} + +int cdns2_halt_endpoint(struct cdns2_device *pdev, + struct cdns2_endpoint *pep, + int value) +{ + u8 __iomem *conf; + int dir = 0; + + if (!(pep->ep_state & EP_ENABLED)) + return -EPERM; + + if (pep->dir) { + dir = ENDPRST_IO_TX; + conf = &pdev->epx_regs->ep[pep->num - 1].txcon; + } else { + conf = &pdev->epx_regs->ep[pep->num - 1].rxcon; + } + + if (!value) { + struct cdns2_trb *trb = NULL; + struct cdns2_request *preq; + struct cdns2_trb trb_tmp; + + preq = cdns2_next_preq(&pep->pending_list); + if (preq) { + trb = preq->trb; + if (trb) { + trb_tmp = *trb; + trb->control = trb->control ^ cpu_to_le32(TRB_CYCLE); + } + } + + /* Resets Sequence Number */ + writeb(dir | pep->num, &pdev->epx_regs->endprst); + writeb(dir | ENDPRST_TOGRST | pep->num, + &pdev->epx_regs->endprst); + + clear_reg_bit_8(conf, EPX_CON_STALL); + + pep->ep_state &= ~(EP_STALLED | EP_STALL_PENDING); + + if (preq) { + if (trb) + *trb = trb_tmp; + + cdns2_rearm_transfer(pep, 1); + } + + cdns2_start_all_request(pdev, pep); + } else { + set_reg_bit_8(conf, EPX_CON_STALL); + writeb(dir | pep->num, &pdev->epx_regs->endprst); + writeb(dir | ENDPRST_FIFORST | pep->num, + &pdev->epx_regs->endprst); + pep->ep_state |= EP_STALLED; + } + + return 0; +} + +/* Sets/clears stall on selected endpoint. */ +static int cdns2_gadget_ep_set_halt(struct usb_ep *ep, int value) +{ + struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep); + struct cdns2_device *pdev = pep->pdev; + struct cdns2_request *preq; + unsigned long flags = 0; + int ret; + + spin_lock_irqsave(&pdev->lock, flags); + + preq = cdns2_next_preq(&pep->pending_list); + if (value && preq) { + ret = -EAGAIN; + goto done; + } + + if (!value) + pep->ep_state &= ~EP_WEDGE; + + ret = cdns2_halt_endpoint(pdev, pep, value); + +done: + spin_unlock_irqrestore(&pdev->lock, flags); + return ret; +} + +static int cdns2_gadget_ep_set_wedge(struct usb_ep *ep) +{ + struct cdns2_endpoint *pep = ep_to_cdns2_ep(ep); + + cdns2_gadget_ep_set_halt(ep, 1); + pep->ep_state |= EP_WEDGE; + + return 0; +} + +static struct +cdns2_endpoint *cdns2_find_available_ep(struct cdns2_device *pdev, + struct usb_endpoint_descriptor *desc) +{ + struct cdns2_endpoint *pep; + struct usb_ep *ep; + int ep_correct; + + list_for_each_entry(ep, &pdev->gadget.ep_list, ep_list) { + unsigned long num; + int ret; + /* ep name pattern likes epXin or epXout. */ + char c[2] = {ep->name[2], '\0'}; + + ret = kstrtoul(c, 10, &num); + if (ret) + return ERR_PTR(ret); + pep = ep_to_cdns2_ep(ep); + + if (pep->num != num) + continue; + + ep_correct = (pep->endpoint.caps.dir_in && + usb_endpoint_dir_in(desc)) || + (pep->endpoint.caps.dir_out && + usb_endpoint_dir_out(desc)); + + if (ep_correct && !(pep->ep_state & EP_CLAIMED)) + return pep; + } + + return ERR_PTR(-ENOENT); +} + +/* + * Function used to recognize which endpoints will be used to optimize + * on-chip memory usage. + */ +static struct +usb_ep *cdns2_gadget_match_ep(struct usb_gadget *gadget, + struct usb_endpoint_descriptor *desc, + struct usb_ss_ep_comp_descriptor *comp_desc) +{ + struct cdns2_device *pdev = gadget_to_cdns2_device(gadget); + struct cdns2_endpoint *pep; + unsigned long flags; + + pep = cdns2_find_available_ep(pdev, desc); + if (IS_ERR(pep)) { + dev_err(pdev->dev, "no available ep\n"); + return NULL; + } + + spin_lock_irqsave(&pdev->lock, flags); + + if (usb_endpoint_type(desc) == USB_ENDPOINT_XFER_ISOC) + pep->buffering = 4; + else + pep->buffering = 1; + + pep->ep_state |= EP_CLAIMED; + spin_unlock_irqrestore(&pdev->lock, flags); + + return &pep->endpoint; +} + +static const struct usb_ep_ops cdns2_gadget_ep_ops = { + .enable = cdns2_gadget_ep_enable, + .disable = cdns2_gadget_ep_disable, + .alloc_request = cdns2_gadget_ep_alloc_request, + .free_request = cdns2_gadget_ep_free_request, + .queue = cdns2_gadget_ep_queue, + .dequeue = cdns2_gadget_ep_dequeue, + .set_halt = cdns2_gadget_ep_set_halt, + .set_wedge = cdns2_gadget_ep_set_wedge, +}; + +static int cdns2_gadget_get_frame(struct usb_gadget *gadget) +{ + struct cdns2_device *pdev = gadget_to_cdns2_device(gadget); + + return readw(&pdev->usb_regs->frmnr); +} + +static int cdns2_gadget_wakeup(struct usb_gadget *gadget) +{ + struct cdns2_device *pdev = gadget_to_cdns2_device(gadget); + unsigned long flags; + + spin_lock_irqsave(&pdev->lock, flags); + cdns2_wakeup(pdev); + spin_unlock_irqrestore(&pdev->lock, flags); + + return 0; +} + +static int cdns2_gadget_set_selfpowered(struct usb_gadget *gadget, + int is_selfpowered) +{ + struct cdns2_device *pdev = gadget_to_cdns2_device(gadget); + unsigned long flags; + + spin_lock_irqsave(&pdev->lock, flags); + pdev->is_selfpowered = !!is_selfpowered; + spin_unlock_irqrestore(&pdev->lock, flags); + return 0; +} + +/* Disable interrupts and begin the controller halting process. */ +static void cdns2_quiesce(struct cdns2_device *pdev) +{ + set_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_DISCON); + + /* Disable interrupt. */ + writeb(0, &pdev->interrupt_regs->extien), + writeb(0, &pdev->interrupt_regs->usbien), + writew(0, &pdev->adma_regs->ep_ien); + + /* Clear interrupt line. */ + writeb(0x0, &pdev->interrupt_regs->usbirq); +} + +static void cdns2_gadget_config(struct cdns2_device *pdev) +{ + cdns2_ep0_config(pdev); + + /* Enable DMA interrupts for all endpoints. */ + writel(~0x0, &pdev->adma_regs->ep_ien); + cdns2_enable_l1(pdev, 0); + writeb(USB_IEN_INIT, &pdev->interrupt_regs->usbien); + writeb(EXTIRQ_WAKEUP, &pdev->interrupt_regs->extien); + writel(DMA_CONF_DMULT, &pdev->adma_regs->conf); +} + +static int cdns2_gadget_pullup(struct usb_gadget *gadget, int is_on) +{ + struct cdns2_device *pdev = gadget_to_cdns2_device(gadget); + unsigned long flags; + + /* + * Disable events handling while controller is being + * enabled/disabled. + */ + disable_irq(pdev->irq); + spin_lock_irqsave(&pdev->lock, flags); + + if (is_on) { + cdns2_gadget_config(pdev); + clear_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_DISCON); + } else { + cdns2_quiesce(pdev); + } + + spin_unlock_irqrestore(&pdev->lock, flags); + enable_irq(pdev->irq); + + return 0; +} + +static int cdns2_gadget_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct cdns2_device *pdev = gadget_to_cdns2_device(gadget); + enum usb_device_speed max_speed = driver->max_speed; + unsigned long flags; + + spin_lock_irqsave(&pdev->lock, flags); + pdev->gadget_driver = driver; + + /* Limit speed if necessary. */ + max_speed = min(driver->max_speed, gadget->max_speed); + + switch (max_speed) { + case USB_SPEED_FULL: + writeb(SPEEDCTRL_HSDISABLE, &pdev->usb_regs->speedctrl); + break; + case USB_SPEED_HIGH: + writeb(0, &pdev->usb_regs->speedctrl); + break; + default: + dev_err(pdev->dev, "invalid maximum_speed parameter %d\n", + max_speed); + fallthrough; + case USB_SPEED_UNKNOWN: + /* Default to highspeed. */ + max_speed = USB_SPEED_HIGH; + break; + } + + /* Reset all USB endpoints. */ + writeb(ENDPRST_IO_TX, &pdev->usb_regs->endprst); + writeb(ENDPRST_FIFORST | ENDPRST_TOGRST | ENDPRST_IO_TX, + &pdev->usb_regs->endprst); + writeb(ENDPRST_FIFORST | ENDPRST_TOGRST, &pdev->usb_regs->endprst); + + cdns2_eps_onchip_buffer_init(pdev); + + cdns2_gadget_config(pdev); + spin_unlock_irqrestore(&pdev->lock, flags); + + return 0; +} + +static int cdns2_gadget_udc_stop(struct usb_gadget *gadget) +{ + struct cdns2_device *pdev = gadget_to_cdns2_device(gadget); + struct cdns2_endpoint *pep; + u32 bEndpointAddress; + struct usb_ep *ep; + int val; + + pdev->gadget_driver = NULL; + pdev->gadget.speed = USB_SPEED_UNKNOWN; + + list_for_each_entry(ep, &pdev->gadget.ep_list, ep_list) { + pep = ep_to_cdns2_ep(ep); + bEndpointAddress = pep->num | pep->dir; + cdns2_select_ep(pdev, bEndpointAddress); + writel(DMA_EP_CMD_EPRST, &pdev->adma_regs->ep_cmd); + readl_poll_timeout_atomic(&pdev->adma_regs->ep_cmd, val, + !(val & DMA_EP_CMD_EPRST), 1, 100); + } + + cdns2_quiesce(pdev); + + writeb(ENDPRST_IO_TX, &pdev->usb_regs->endprst); + writeb(ENDPRST_FIFORST | ENDPRST_TOGRST | ENDPRST_IO_TX, + &pdev->epx_regs->endprst); + writeb(ENDPRST_FIFORST | ENDPRST_TOGRST, &pdev->epx_regs->endprst); + + return 0; +} + +static const struct usb_gadget_ops cdns2_gadget_ops = { + .get_frame = cdns2_gadget_get_frame, + .wakeup = cdns2_gadget_wakeup, + .set_selfpowered = cdns2_gadget_set_selfpowered, + .pullup = cdns2_gadget_pullup, + .udc_start = cdns2_gadget_udc_start, + .udc_stop = cdns2_gadget_udc_stop, + .match_ep = cdns2_gadget_match_ep, +}; + +static void cdns2_free_all_eps(struct cdns2_device *pdev) +{ + int i; + + for (i = 0; i < CDNS2_ENDPOINTS_NUM; i++) + cdns2_free_tr_segment(&pdev->eps[i]); +} + +/* Initializes software endpoints of gadget. */ +static int cdns2_init_eps(struct cdns2_device *pdev) +{ + struct cdns2_endpoint *pep; + int i; + + for (i = 0; i < CDNS2_ENDPOINTS_NUM; i++) { + bool direction = !(i & 1); /* Start from OUT endpoint. */ + u8 epnum = ((i + 1) >> 1); + + /* + * Endpoints are being held in pdev->eps[] in form: + * ep0, ep1out, ep1in ... ep15out, ep15in. + */ + if (!CDNS2_IF_EP_EXIST(pdev, epnum, direction)) + continue; + + pep = &pdev->eps[i]; + pep->pdev = pdev; + pep->num = epnum; + /* 0 for OUT, 1 for IN. */ + pep->dir = direction ? USB_DIR_IN : USB_DIR_OUT; + pep->idx = i; + + /* Ep0in and ep0out are represented by pdev->eps[0]. */ + if (!epnum) { + int ret; + + snprintf(pep->name, sizeof(pep->name), "ep%d%s", + epnum, "BiDir"); + + cdns2_init_ep0(pdev, pep); + + ret = cdns2_alloc_tr_segment(pep); + if (ret) { + dev_err(pdev->dev, "Failed to init ep0\n"); + return ret; + } + } else { + snprintf(pep->name, sizeof(pep->name), "ep%d%s", + epnum, !!direction ? "in" : "out"); + pep->endpoint.name = pep->name; + + usb_ep_set_maxpacket_limit(&pep->endpoint, 1024); + pep->endpoint.ops = &cdns2_gadget_ep_ops; + list_add_tail(&pep->endpoint.ep_list, &pdev->gadget.ep_list); + + pep->endpoint.caps.dir_in = direction; + pep->endpoint.caps.dir_out = !direction; + + pep->endpoint.caps.type_iso = 1; + pep->endpoint.caps.type_bulk = 1; + pep->endpoint.caps.type_int = 1; + } + + pep->endpoint.name = pep->name; + pep->ep_state = 0; + + dev_dbg(pdev->dev, "Init %s, SupType: CTRL: %s, INT: %s, " + "BULK: %s, ISOC %s, SupDir IN: %s, OUT: %s\n", + pep->name, + (pep->endpoint.caps.type_control) ? "yes" : "no", + (pep->endpoint.caps.type_int) ? "yes" : "no", + (pep->endpoint.caps.type_bulk) ? "yes" : "no", + (pep->endpoint.caps.type_iso) ? "yes" : "no", + (pep->endpoint.caps.dir_in) ? "yes" : "no", + (pep->endpoint.caps.dir_out) ? "yes" : "no"); + + INIT_LIST_HEAD(&pep->pending_list); + INIT_LIST_HEAD(&pep->deferred_list); + } + + return 0; +} + +static int cdns2_gadget_start(struct cdns2_device *pdev) +{ + u32 max_speed; + void *buf; + int val; + int ret; + + pdev->usb_regs = pdev->regs; + pdev->ep0_regs = pdev->regs; + pdev->epx_regs = pdev->regs; + pdev->interrupt_regs = pdev->regs; + pdev->adma_regs = pdev->regs + CDNS2_ADMA_REGS_OFFSET; + + /* Reset controller. */ + set_reg_bit_8(&pdev->usb_regs->cpuctrl, CPUCTRL_SW_RST); + + ret = readl_poll_timeout_atomic(&pdev->usb_regs->cpuctrl, val, + !(val & CPUCTRL_SW_RST), 1, 10000); + if (ret) { + dev_err(pdev->dev, "Error: reset controller timeout\n"); + return -EINVAL; + } + + usb_initialize_gadget(pdev->dev, &pdev->gadget, NULL); + + device_property_read_u16(pdev->dev, "cdns,on-chip-tx-buff-size", + &pdev->onchip_tx_buf); + device_property_read_u16(pdev->dev, "cdns,on-chip-rx-buff-size", + &pdev->onchip_rx_buf); + device_property_read_u32(pdev->dev, "cdns,avail-endpoints", + &pdev->eps_supported); + + /* + * Driver assumes that each USBHS controller has at least + * one IN and one OUT non control endpoint. + */ + if (!pdev->onchip_tx_buf && !pdev->onchip_rx_buf) { + ret = -EINVAL; + dev_err(pdev->dev, "Invalid on-chip memory configuration\n"); + goto put_gadget; + } + + if (!(pdev->eps_supported & ~0x00010001)) { + ret = -EINVAL; + dev_err(pdev->dev, "No hardware endpoints available\n"); + goto put_gadget; + } + + max_speed = usb_get_maximum_speed(pdev->dev); + + switch (max_speed) { + case USB_SPEED_FULL: + case USB_SPEED_HIGH: + break; + default: + dev_err(pdev->dev, "invalid maximum_speed parameter %d\n", + max_speed); + fallthrough; + case USB_SPEED_UNKNOWN: + max_speed = USB_SPEED_HIGH; + break; + } + + pdev->gadget.max_speed = max_speed; + pdev->gadget.speed = USB_SPEED_UNKNOWN; + pdev->gadget.ops = &cdns2_gadget_ops; + pdev->gadget.name = "usbhs-gadget"; + pdev->gadget.quirk_avoids_skb_reserve = 1; + pdev->gadget.irq = pdev->irq; + + spin_lock_init(&pdev->lock); + INIT_WORK(&pdev->pending_status_wq, cdns2_pending_setup_status_handler); + + /* Initialize endpoint container. */ + INIT_LIST_HEAD(&pdev->gadget.ep_list); + pdev->eps_dma_pool = dma_pool_create("cdns2_eps_dma_pool", pdev->dev, + TR_SEG_SIZE, 8, 0); + if (!pdev->eps_dma_pool) { + dev_err(pdev->dev, "Failed to create TRB dma pool\n"); + ret = -ENOMEM; + goto put_gadget; + } + + ret = cdns2_init_eps(pdev); + if (ret) { + dev_err(pdev->dev, "Failed to create endpoints\n"); + goto destroy_dma_pool; + } + + pdev->gadget.sg_supported = 1; + + pdev->zlp_buf = kzalloc(CDNS2_EP_ZLP_BUF_SIZE, GFP_KERNEL); + if (!pdev->zlp_buf) { + ret = -ENOMEM; + goto destroy_dma_pool; + } + + /* Allocate memory for setup packet buffer. */ + buf = dma_alloc_coherent(pdev->dev, 8, &pdev->ep0_preq.request.dma, + GFP_DMA); + pdev->ep0_preq.request.buf = buf; + + if (!pdev->ep0_preq.request.buf) { + ret = -ENOMEM; + goto free_zlp_buf; + } + + /* Add USB gadget device. */ + ret = usb_add_gadget(&pdev->gadget); + if (ret < 0) { + dev_err(pdev->dev, "Failed to add gadget\n"); + goto free_ep0_buf; + } + + return 0; + +free_ep0_buf: + dma_free_coherent(pdev->dev, 8, pdev->ep0_preq.request.buf, + pdev->ep0_preq.request.dma); +free_zlp_buf: + kfree(pdev->zlp_buf); +destroy_dma_pool: + dma_pool_destroy(pdev->eps_dma_pool); +put_gadget: + usb_put_gadget(&pdev->gadget); + + return ret; +} + +int cdns2_gadget_suspend(struct cdns2_device *pdev) +{ + unsigned long flags; + + cdns2_disconnect_gadget(pdev); + + spin_lock_irqsave(&pdev->lock, flags); + pdev->gadget.speed = USB_SPEED_UNKNOWN; + + usb_gadget_set_state(&pdev->gadget, USB_STATE_NOTATTACHED); + cdns2_enable_l1(pdev, 0); + + /* Disable interrupt for device. */ + writeb(0, &pdev->interrupt_regs->usbien); + writel(0, &pdev->adma_regs->ep_ien); + spin_unlock_irqrestore(&pdev->lock, flags); + + return 0; +} + +int cdns2_gadget_resume(struct cdns2_device *pdev, bool hibernated) +{ + unsigned long flags; + + spin_lock_irqsave(&pdev->lock, flags); + + if (!pdev->gadget_driver) { + spin_unlock_irqrestore(&pdev->lock, flags); + return 0; + } + + cdns2_gadget_config(pdev); + + if (hibernated) + clear_reg_bit_8(&pdev->usb_regs->usbcs, USBCS_DISCON); + + spin_unlock_irqrestore(&pdev->lock, flags); + + return 0; +} + +void cdns2_gadget_remove(struct cdns2_device *pdev) +{ + pm_runtime_mark_last_busy(pdev->dev); + pm_runtime_put_autosuspend(pdev->dev); + + usb_del_gadget(&pdev->gadget); + cdns2_free_all_eps(pdev); + + dma_pool_destroy(pdev->eps_dma_pool); + kfree(pdev->zlp_buf); + usb_put_gadget(&pdev->gadget); +} + +int cdns2_gadget_init(struct cdns2_device *pdev) +{ + int ret; + + /* Ensure 32-bit DMA Mask. */ + ret = dma_set_mask_and_coherent(pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(pdev->dev, "Failed to set dma mask: %d\n", ret); + return ret; + } + + pm_runtime_get_sync(pdev->dev); + + cdsn2_isoc_burst_opt(pdev); + + ret = cdns2_gadget_start(pdev); + if (ret) { + pm_runtime_put_sync(pdev->dev); + return ret; + } + + /* + * Because interrupt line can be shared with other components in + * driver it can't use IRQF_ONESHOT flag here. + */ + ret = devm_request_threaded_irq(pdev->dev, pdev->irq, + cdns2_usb_irq_handler, + cdns2_thread_irq_handler, + IRQF_SHARED, + dev_name(pdev->dev), + pdev); + if (ret) + goto err0; + + return 0; + +err0: + cdns2_gadget_remove(pdev); + + return ret; +} diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-pci.c b/drivers/usb/gadget/udc/cdns2/cdns2-pci.c new file mode 100644 index 000000000000..1691541c9413 --- /dev/null +++ b/drivers/usb/gadget/udc/cdns2/cdns2-pci.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cadence USBHS-DEV controller - PCI Glue driver. + * + * Copyright (C) 2023 Cadence. + * + * Author: Pawel Laszczak + * + */ + +#include +#include +#include + +#include "cdns2-gadget.h" + +#define PCI_DRIVER_NAME "cdns-pci-usbhs" +#define CDNS_VENDOR_ID 0x17cd +#define CDNS_DEVICE_ID 0x0120 +#define PCI_BAR_DEV 0 +#define PCI_DEV_FN_DEVICE 0 + +static int cdns2_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + resource_size_t rsrc_start, rsrc_len; + struct device *dev = &pdev->dev; + struct cdns2_device *priv_dev; + struct resource *res; + int ret; + + /* For GADGET PCI (devfn) function number is 0. */ + if (!id || pdev->devfn != PCI_DEV_FN_DEVICE || + pdev->class != PCI_CLASS_SERIAL_USB_DEVICE) + return -EINVAL; + + ret = pcim_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", ret); + return ret; + } + + pci_set_master(pdev); + + priv_dev = devm_kzalloc(&pdev->dev, sizeof(*priv_dev), GFP_KERNEL); + if (!priv_dev) + return -ENOMEM; + + dev_dbg(dev, "Initialize resources\n"); + rsrc_start = pci_resource_start(pdev, PCI_BAR_DEV); + rsrc_len = pci_resource_len(pdev, PCI_BAR_DEV); + + res = devm_request_mem_region(dev, rsrc_start, rsrc_len, "dev"); + if (!res) { + dev_dbg(dev, "controller already in use\n"); + return -EBUSY; + } + + priv_dev->regs = devm_ioremap(dev, rsrc_start, rsrc_len); + if (!priv_dev->regs) { + dev_dbg(dev, "error mapping memory\n"); + return -EFAULT; + } + + priv_dev->irq = pdev->irq; + dev_dbg(dev, "USBSS-DEV physical base addr: %pa\n", + &rsrc_start); + + priv_dev->dev = dev; + + priv_dev->eps_supported = 0x000f000f; + priv_dev->onchip_tx_buf = 16; + priv_dev->onchip_rx_buf = 16; + + ret = cdns2_gadget_init(priv_dev); + if (ret) + return ret; + + pci_set_drvdata(pdev, priv_dev); + + device_wakeup_enable(&pdev->dev); + if (pci_dev_run_wake(pdev)) + pm_runtime_put_noidle(&pdev->dev); + + return 0; +} + +static void cdns2_pci_remove(struct pci_dev *pdev) +{ + struct cdns2_device *priv_dev = pci_get_drvdata(pdev); + + if (pci_dev_run_wake(pdev)) + pm_runtime_get_noresume(&pdev->dev); + + cdns2_gadget_remove(priv_dev); +} + +static int cdns2_pci_suspend(struct device *dev) +{ + struct cdns2_device *priv_dev = dev_get_drvdata(dev); + + return cdns2_gadget_suspend(priv_dev); +} + +static int cdns2_pci_resume(struct device *dev) +{ + struct cdns2_device *priv_dev = dev_get_drvdata(dev); + + return cdns2_gadget_resume(priv_dev, 1); +} + +static const struct dev_pm_ops cdns2_pci_pm_ops = { + SYSTEM_SLEEP_PM_OPS(cdns2_pci_suspend, cdns2_pci_resume) +}; + +static const struct pci_device_id cdns2_pci_ids[] = { + { PCI_VENDOR_ID_CDNS, CDNS_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_SERIAL_USB_DEVICE, PCI_ANY_ID }, + { 0, } +}; + +static struct pci_driver cdns2_pci_driver = { + .name = "cdns2-pci", + .id_table = &cdns2_pci_ids[0], + .probe = cdns2_pci_probe, + .remove = cdns2_pci_remove, + .driver = { + .pm = pm_ptr(&cdns2_pci_pm_ops), + } +}; + +module_pci_driver(cdns2_pci_driver); +MODULE_DEVICE_TABLE(pci, cdns2_pci_ids); + +MODULE_ALIAS("pci:cdns2"); +MODULE_AUTHOR("Pawel Laszczak "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Cadence CDNS2 PCI driver"); -- cgit v1.2.3 From 07a3aef249a1a084b081002e7cbab3873dfb58ae Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Fri, 2 Jun 2023 06:26:43 -0400 Subject: usb: cdns2: Add tracepoints for CDNS2 driver Patch adds the series of tracepoints that can be used for debugging issues detected in driver. Signed-off-by: Pawel Laszczak Message-ID: <20230602102644.77470-4-pawell@cadence.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/cdns2/Makefile | 2 + drivers/usb/gadget/udc/cdns2/cdns2-debug.h | 203 ++++++++++ drivers/usb/gadget/udc/cdns2/cdns2-ep0.c | 26 +- drivers/usb/gadget/udc/cdns2/cdns2-gadget.c | 52 ++- drivers/usb/gadget/udc/cdns2/cdns2-trace.c | 11 + drivers/usb/gadget/udc/cdns2/cdns2-trace.h | 605 ++++++++++++++++++++++++++++ 6 files changed, 896 insertions(+), 3 deletions(-) create mode 100644 drivers/usb/gadget/udc/cdns2/cdns2-debug.h create mode 100644 drivers/usb/gadget/udc/cdns2/cdns2-trace.c create mode 100644 drivers/usb/gadget/udc/cdns2/cdns2-trace.h diff --git a/drivers/usb/gadget/udc/cdns2/Makefile b/drivers/usb/gadget/udc/cdns2/Makefile index 7c746e6d53c2..a1ffbbe2e768 100644 --- a/drivers/usb/gadget/udc/cdns2/Makefile +++ b/drivers/usb/gadget/udc/cdns2/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 # define_trace.h needs to know how to find our header +CFLAGS_cdns2-trace.o := -I$(src) obj-$(CONFIG_USB_CDNS2_UDC) += cdns2-udc-pci.o cdns2-udc-pci-$(CONFIG_USB_CDNS2_UDC) += cdns2-pci.o cdns2-gadget.o cdns2-ep0.o +cdns2-udc-pci-$(CONFIG_TRACING) += cdns2-trace.o diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-debug.h b/drivers/usb/gadget/udc/cdns2/cdns2-debug.h new file mode 100644 index 000000000000..fd22ae949008 --- /dev/null +++ b/drivers/usb/gadget/udc/cdns2/cdns2-debug.h @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Cadence USBHS-DEV Driver. + * Debug header file. + * + * Copyright (C) 2023 Cadence. + * + * Author: Pawel Laszczak + */ + +#ifndef __LINUX_CDNS2_DEBUG +#define __LINUX_CDNS2_DEBUG + +static inline const char *cdns2_decode_usb_irq(char *str, size_t size, + u8 usb_irq, u8 ext_irq) +{ + int ret; + + ret = snprintf(str, size, "usbirq: 0x%02x - ", usb_irq); + + if (usb_irq & USBIRQ_SOF) + ret += snprintf(str + ret, size - ret, "SOF "); + if (usb_irq & USBIRQ_SUTOK) + ret += snprintf(str + ret, size - ret, "SUTOK "); + if (usb_irq & USBIRQ_SUDAV) + ret += snprintf(str + ret, size - ret, "SETUP "); + if (usb_irq & USBIRQ_SUSPEND) + ret += snprintf(str + ret, size - ret, "Suspend "); + if (usb_irq & USBIRQ_URESET) + ret += snprintf(str + ret, size - ret, "Reset "); + if (usb_irq & USBIRQ_HSPEED) + ret += snprintf(str + ret, size - ret, "HS "); + if (usb_irq & USBIRQ_LPM) + ret += snprintf(str + ret, size - ret, "LPM "); + + ret += snprintf(str + ret, size - ret, ", EXT: 0x%02x - ", ext_irq); + + if (ext_irq & EXTIRQ_WAKEUP) + ret += snprintf(str + ret, size - ret, "Wakupe "); + if (ext_irq & EXTIRQ_VBUSFAULT_FALL) + ret += snprintf(str + ret, size - ret, "VBUS_FALL "); + if (ext_irq & EXTIRQ_VBUSFAULT_RISE) + ret += snprintf(str + ret, size - ret, "VBUS_RISE "); + + if (ret >= size) + pr_info("CDNS2: buffer overflowed.\n"); + + return str; +} + +static inline const char *cdns2_decode_dma_irq(char *str, size_t size, + u32 ep_ists, u32 ep_sts, + const char *ep_name) +{ + int ret; + + ret = snprintf(str, size, "ISTS: %08x, %s: %08x ", + ep_ists, ep_name, ep_sts); + + if (ep_sts & DMA_EP_STS_IOC) + ret += snprintf(str + ret, size - ret, "IOC "); + if (ep_sts & DMA_EP_STS_ISP) + ret += snprintf(str + ret, size - ret, "ISP "); + if (ep_sts & DMA_EP_STS_DESCMIS) + ret += snprintf(str + ret, size - ret, "DESCMIS "); + if (ep_sts & DMA_EP_STS_TRBERR) + ret += snprintf(str + ret, size - ret, "TRBERR "); + if (ep_sts & DMA_EP_STS_OUTSMM) + ret += snprintf(str + ret, size - ret, "OUTSMM "); + if (ep_sts & DMA_EP_STS_ISOERR) + ret += snprintf(str + ret, size - ret, "ISOERR "); + if (ep_sts & DMA_EP_STS_DBUSY) + ret += snprintf(str + ret, size - ret, "DBUSY "); + if (DMA_EP_STS_CCS(ep_sts)) + ret += snprintf(str + ret, size - ret, "CCS "); + + if (ret >= size) + pr_info("CDNS2: buffer overflowed.\n"); + + return str; +} + +static inline const char *cdns2_decode_epx_irq(char *str, size_t size, + char *ep_name, u32 ep_ists, + u32 ep_sts) +{ + return cdns2_decode_dma_irq(str, size, ep_ists, ep_sts, ep_name); +} + +static inline const char *cdns2_decode_ep0_irq(char *str, size_t size, + u32 ep_ists, u32 ep_sts, + int dir) +{ + return cdns2_decode_dma_irq(str, size, ep_ists, ep_sts, + dir ? "ep0IN" : "ep0OUT"); +} + +static inline const char *cdns2_raw_ring(struct cdns2_endpoint *pep, + struct cdns2_trb *trbs, + char *str, size_t size) +{ + struct cdns2_ring *ring = &pep->ring; + struct cdns2_trb *trb; + dma_addr_t dma; + int ret; + int i; + + ret = snprintf(str, size, "\n\t\tTR for %s:", pep->name); + + trb = &trbs[ring->dequeue]; + dma = cdns2_trb_virt_to_dma(pep, trb); + ret += snprintf(str + ret, size - ret, + "\n\t\tRing deq index: %d, trb: V=%p, P=0x%pad\n", + ring->dequeue, trb, &dma); + + trb = &trbs[ring->enqueue]; + dma = cdns2_trb_virt_to_dma(pep, trb); + ret += snprintf(str + ret, size - ret, + "\t\tRing enq index: %d, trb: V=%p, P=0x%pad\n", + ring->enqueue, trb, &dma); + + ret += snprintf(str + ret, size - ret, + "\t\tfree trbs: %d, CCS=%d, PCS=%d\n", + ring->free_trbs, ring->ccs, ring->pcs); + + if (TRBS_PER_SEGMENT > 40) { + ret += snprintf(str + ret, size - ret, + "\t\tTransfer ring %d too big\n", TRBS_PER_SEGMENT); + return str; + } + + dma = ring->dma; + for (i = 0; i < TRBS_PER_SEGMENT; ++i) { + trb = &trbs[i]; + ret += snprintf(str + ret, size - ret, + "\t\t@%pad %08x %08x %08x\n", &dma, + le32_to_cpu(trb->buffer), + le32_to_cpu(trb->length), + le32_to_cpu(trb->control)); + dma += sizeof(*trb); + } + + if (ret >= size) + pr_info("CDNS2: buffer overflowed.\n"); + + return str; +} + +static inline const char *cdns2_trb_type_string(u8 type) +{ + switch (type) { + case TRB_NORMAL: + return "Normal"; + case TRB_LINK: + return "Link"; + default: + return "UNKNOWN"; + } +} + +static inline const char *cdns2_decode_trb(char *str, size_t size, u32 flags, + u32 length, u32 buffer) +{ + int type = TRB_FIELD_TO_TYPE(flags); + int ret; + + switch (type) { + case TRB_LINK: + ret = snprintf(str, size, + "LINK %08x type '%s' flags %c:%c:%c%c:%c", + buffer, cdns2_trb_type_string(type), + flags & TRB_CYCLE ? 'C' : 'c', + flags & TRB_TOGGLE ? 'T' : 't', + flags & TRB_CHAIN ? 'C' : 'c', + flags & TRB_CHAIN ? 'H' : 'h', + flags & TRB_IOC ? 'I' : 'i'); + break; + case TRB_NORMAL: + ret = snprintf(str, size, + "type: '%s', Buffer: %08x, length: %ld, burst len: %ld, " + "flags %c:%c:%c%c:%c", + cdns2_trb_type_string(type), + buffer, TRB_LEN(length), + TRB_FIELD_TO_BURST(length), + flags & TRB_CYCLE ? 'C' : 'c', + flags & TRB_ISP ? 'I' : 'i', + flags & TRB_CHAIN ? 'C' : 'c', + flags & TRB_CHAIN ? 'H' : 'h', + flags & TRB_IOC ? 'I' : 'i'); + break; + default: + ret = snprintf(str, size, "type '%s' -> raw %08x %08x %08x", + cdns2_trb_type_string(type), + buffer, length, flags); + } + + if (ret >= size) + pr_info("CDNS2: buffer overflowed.\n"); + + return str; +} + +#endif /*__LINUX_CDNS2_DEBUG*/ diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-ep0.c b/drivers/usb/gadget/udc/cdns2/cdns2-ep0.c index 133d5acaf502..fa12a5d46f2e 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-ep0.c +++ b/drivers/usb/gadget/udc/cdns2/cdns2-ep0.c @@ -11,6 +11,7 @@ #include #include "cdns2-gadget.h" +#include "cdns2-trace.h" static struct usb_endpoint_descriptor cdns2_gadget_ep0_desc = { .bLength = USB_DT_ENDPOINT_SIZE, @@ -60,6 +61,8 @@ static void cdns2_ep0_enqueue(struct cdns2_device *pdev, dma_addr_t dma_addr, ring->trbs[1].control = 0; } + trace_cdns2_queue_trb(pep, ring->trbs); + if (!pep->dir) writel(0, &pdev->ep0_regs->rxbc); @@ -68,6 +71,8 @@ static void cdns2_ep0_enqueue(struct cdns2_device *pdev, dma_addr_t dma_addr, writel(DMA_EP_STS_TRBERR, ®s->ep_sts); writel(pep->ring.dma, ®s->ep_traddr); + trace_cdns2_doorbell_ep0(pep, readl(®s->ep_traddr)); + writel(DMA_EP_CMD_DRDY, ®s->ep_cmd); } @@ -128,6 +133,8 @@ static int cdns2_req_ep0_set_configuration(struct cdns2_device *pdev, if (ret) return ret; + trace_cdns2_device_state(config ? "configured" : "addressed"); + if (!config) usb_gadget_set_state(&pdev->gadget, USB_STATE_ADDRESS); @@ -158,6 +165,8 @@ static int cdns2_req_ep0_set_address(struct cdns2_device *pdev, u32 addr) usb_gadget_set_state(&pdev->gadget, (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT)); + trace_cdns2_device_state(addr ? "addressed" : "default"); + return 0; } @@ -385,8 +394,12 @@ void cdns2_handle_setup_packet(struct cdns2_device *pdev) * If SETUP packet was modified while reading just simple ignore it. * The new one will be handled latter. */ - if (cdns2_check_new_setup(pdev)) + if (cdns2_check_new_setup(pdev)) { + trace_cdns2_ep0_setup("overridden"); return; + } + + trace_cdns2_ctrl_req(ctrl); if (!pdev->gadget_driver) goto out; @@ -431,8 +444,10 @@ void cdns2_handle_setup_packet(struct cdns2_device *pdev) else ret = cdns2_ep0_delegate_req(pdev); - if (ret == USB_GADGET_DELAYED_STATUS) + if (ret == USB_GADGET_DELAYED_STATUS) { + trace_cdns2_ep0_status_stage("delayed"); return; + } out: if (ret < 0) @@ -448,6 +463,7 @@ static void cdns2_transfer_completed(struct cdns2_device *pdev) if (!list_empty(&pep->pending_list)) { struct cdns2_request *preq; + trace_cdns2_complete_trb(pep, pep->ring.trbs); preq = cdns2_next_preq(&pep->pending_list); preq->request.actual = @@ -464,6 +480,8 @@ void cdns2_handle_ep0_interrupt(struct cdns2_device *pdev, int dir) cdns2_select_ep(pdev, dir); + trace_cdns2_ep0_irq(pdev); + ep_sts_reg = readl(&pdev->adma_regs->ep_sts); writel(ep_sts_reg, &pdev->adma_regs->ep_sts); @@ -530,8 +548,11 @@ static int cdns2_gadget_ep0_queue(struct usb_ep *ep, preq = to_cdns2_request(request); + trace_cdns2_request_enqueue(preq); + /* Cancel the request if controller receive new SETUP packet. */ if (cdns2_check_new_setup(pdev)) { + trace_cdns2_ep0_setup("overridden"); spin_unlock_irqrestore(&pdev->lock, flags); return -ECONNRESET; } @@ -556,6 +577,7 @@ static int cdns2_gadget_ep0_queue(struct usb_ep *ep, } if (!list_empty(&pep->pending_list)) { + trace_cdns2_ep0_setup("pending"); dev_err(pdev->dev, "can't handle multiple requests for ep0\n"); spin_unlock_irqrestore(&pdev->lock, flags); diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c index 66680e189d97..0eed0e03842c 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c +++ b/drivers/usb/gadget/udc/cdns2/cdns2-gadget.c @@ -33,6 +33,7 @@ #include #include "cdns2-gadget.h" +#include "cdns2-trace.h" /** * set_reg_bit_32 - set bit in given 32 bits register. @@ -154,6 +155,8 @@ static void cdns2_ep_stall_flush(struct cdns2_endpoint *pep) struct cdns2_device *pdev = pep->pdev; int val; + trace_cdns2_ep_halt(pep, 1, 1); + writel(DMA_EP_CMD_DFLUSH, &pdev->adma_regs->ep_cmd); /* Wait for DFLUSH cleared. */ @@ -247,6 +250,8 @@ void cdns2_gadget_giveback(struct cdns2_endpoint *pep, /* All TRBs have finished, clear the counter. */ preq->finished_trb = 0; + trace_cdns2_request_giveback(preq); + if (request->complete) { spin_unlock(&pdev->lock); usb_gadget_giveback_request(&pep->endpoint, request); @@ -261,6 +266,8 @@ static void cdns2_wa1_restore_cycle_bit(struct cdns2_endpoint *pep) { /* Work around for stale data address in TRB. */ if (pep->wa1_set) { + trace_cdns2_wa1(pep, "restore cycle bit"); + pep->wa1_set = 0; pep->wa1_trb_index = 0xFFFF; if (pep->wa1_cycle_bit) @@ -285,6 +292,7 @@ static int cdns2_wa1_update_guard(struct cdns2_endpoint *pep, pep->wa1_set = 1; pep->wa1_trb = trb; pep->wa1_trb_index = pep->ring.enqueue; + trace_cdns2_wa1(pep, "set guard"); return 0; } } @@ -317,6 +325,7 @@ static int cdns2_prepare_ring(struct cdns2_device *pdev, if (num_trbs > ring->free_trbs) { pep->ep_state |= EP_RING_FULL; + trace_cdns2_no_room_on_ring("Ring full\n"); return -ENOBUFS; } @@ -360,6 +369,7 @@ static void cdns2_dbg_request_trbs(struct cdns2_endpoint *pep, int i = 0; while (i < num_trbs) { + trace_cdns2_queue_trb(pep, trb + i); if (trb + i == link_trb) { trb = pep->ring.trbs; num_trbs = num_trbs - i; @@ -678,6 +688,8 @@ static void cdns2_ep_tx_bulk(struct cdns2_endpoint *pep, static void cdns2_set_drdy(struct cdns2_device *pdev, struct cdns2_endpoint *pep) { + trace_cdns2_ring(pep); + /* * Memory barrier - Cycle Bit must be set before doorbell. */ @@ -692,6 +704,8 @@ static void cdns2_set_drdy(struct cdns2_device *pdev, writel(DMA_EP_STS_TRBERR, &pdev->adma_regs->ep_sts); writel(DMA_EP_CMD_DRDY, &pdev->adma_regs->ep_cmd); } + + trace_cdns2_doorbell_epx(pep, readl(&pdev->adma_regs->ep_traddr)); } static int cdns2_prepare_first_isoc_transfer(struct cdns2_device *pdev, @@ -927,6 +941,7 @@ static bool cdns2_trb_handled(struct cdns2_endpoint *pep, } finish: + trace_cdns2_request_handled(preq, current_index, handled); return handled; } @@ -942,6 +957,7 @@ static void cdns2_skip_isoc_td(struct cdns2_device *pdev, for (i = preq->finished_trb ; i < preq->num_of_trb; i++) { preq->finished_trb++; + trace_cdns2_complete_trb(pep, trb); cdns2_ep_inc_deq(&pep->ring); trb = cdns2_next_trb(pep, trb); } @@ -969,6 +985,7 @@ static void cdns2_transfer_completed(struct cdns2_device *pdev, */ while (TRB_FIELD_TO_TYPE(le32_to_cpu(trb->control)) == TRB_LINK && le32_to_cpu(trb->length)) { + trace_cdns2_complete_trb(pep, trb); cdns2_ep_inc_deq(&pep->ring); trb = pep->ring.trbs + pep->ring.dequeue; } @@ -986,6 +1003,7 @@ static void cdns2_transfer_completed(struct cdns2_device *pdev, request_handled = true; trb = pep->ring.trbs + pep->ring.dequeue; + trace_cdns2_complete_trb(pep, trb); if (pep->dir && pep->type == USB_ENDPOINT_XFER_ISOC) /* @@ -1037,12 +1055,17 @@ static void cdns2_rearm_transfer(struct cdns2_endpoint *pep, u8 rearm) cdns2_wa1_restore_cycle_bit(pep); if (rearm) { + trace_cdns2_ring(pep); + /* Cycle Bit must be updated before arming DMA. */ dma_wmb(); writel(DMA_EP_CMD_DRDY, &pdev->adma_regs->ep_cmd); cdns2_wakeup(pdev); + + trace_cdns2_doorbell_epx(pep, + readl(&pdev->adma_regs->ep_traddr)); } } @@ -1055,6 +1078,8 @@ static void cdns2_handle_epx_interrupt(struct cdns2_endpoint *pep) cdns2_select_ep(pdev, pep->endpoint.address); + trace_cdns2_epx_irq(pdev, pep); + ep_sts_reg = readl(&pdev->adma_regs->ep_sts); writel(ep_sts_reg, &pdev->adma_regs->ep_sts); @@ -1196,6 +1221,8 @@ static irqreturn_t cdns2_thread_usb_irq_handler(struct cdns2_device *pdev) if (!ext_irq && !usb_irq) return IRQ_NONE; + trace_cdns2_usb_irq(usb_irq, ext_irq); + if (ext_irq & EXTIRQ_WAKEUP) { if (pdev->gadget_driver && pdev->gadget_driver->resume) { spin_unlock(&pdev->lock); @@ -1274,6 +1301,8 @@ static irqreturn_t cdns2_thread_irq_handler(int irq, void *data) if (!dma_ep_ists) goto unlock; + trace_cdns2_dma_ep_ists(dma_ep_ists); + /* Handle default endpoint OUT. */ if (dma_ep_ists & DMA_EP_ISTS_EP_OUT0) cdns2_handle_ep0_interrupt(pdev, USB_DIR_OUT); @@ -1461,6 +1490,8 @@ static int cdns2_ep_config(struct cdns2_endpoint *pep, bool enable) if (enable) writel(DMA_EP_CFG_ENABLE, &pdev->adma_regs->ep_cfg); + trace_cdns2_epx_hw_cfg(pdev, pep); + dev_dbg(pdev->dev, "Configure %s: with MPS: %08x, ep con: %02x\n", pep->name, max_packet_size, ep_cfg); @@ -1479,6 +1510,8 @@ struct usb_request *cdns2_gadget_ep_alloc_request(struct usb_ep *ep, preq->pep = pep; + trace_cdns2_alloc_request(preq); + return &preq->request; } @@ -1487,6 +1520,7 @@ void cdns2_gadget_ep_free_request(struct usb_ep *ep, { struct cdns2_request *preq = to_cdns2_request(request); + trace_cdns2_free_request(preq); kfree(preq); } @@ -1552,6 +1586,8 @@ static int cdns2_gadget_ep_enable(struct usb_ep *ep, goto exit; } + trace_cdns2_gadget_ep_enable(pep); + pep->ep_state &= ~(EP_STALLED | EP_STALL_PENDING); pep->ep_state |= EP_ENABLED; pep->wa1_set = 0; @@ -1592,6 +1628,8 @@ static int cdns2_gadget_ep_disable(struct usb_ep *ep) spin_lock_irqsave(&pdev->lock, flags); + trace_cdns2_gadget_ep_disable(pep); + cdns2_select_ep(pdev, ep->desc->bEndpointAddress); clear_reg_bit_32(&pdev->adma_regs->ep_cfg, DMA_EP_CFG_ENABLE); @@ -1640,10 +1678,13 @@ static int cdns2_ep_enqueue(struct cdns2_endpoint *pep, request->status = -EINPROGRESS; ret = usb_gadget_map_request_by_dev(pdev->dev, request, pep->dir); - if (ret) + if (ret) { + trace_cdns2_request_enqueue_error(preq); return ret; + } list_add_tail(&preq->list, &pep->deferred_list); + trace_cdns2_request_enqueue(preq); if (!(pep->ep_state & EP_STALLED) && !(pep->ep_state & EP_STALL_PENDING)) cdns2_start_all_request(pdev, pep); @@ -1722,6 +1763,7 @@ int cdns2_gadget_ep_dequeue(struct usb_ep *ep, spin_lock_irqsave(&pep->pdev->lock, flags); cur_preq = to_cdns2_request(request); + trace_cdns2_request_dequeue(cur_preq); list_for_each_entry_safe(preq, preq_temp, &pep->pending_list, list) { if (cur_preq == preq) { @@ -1758,6 +1800,7 @@ found: & TRB_CYCLE) | TRB_CHAIN | TRB_TYPE(TRB_LINK)); + trace_cdns2_queue_trb(pep, link_trb); link_trb = cdns2_next_trb(pep, link_trb); } @@ -1807,6 +1850,8 @@ int cdns2_halt_endpoint(struct cdns2_device *pdev, } } + trace_cdns2_ep_halt(pep, 0, 0); + /* Resets Sequence Number */ writeb(dir | pep->num, &pdev->epx_regs->endprst); writeb(dir | ENDPRST_TOGRST | pep->num, @@ -1825,6 +1870,7 @@ int cdns2_halt_endpoint(struct cdns2_device *pdev, cdns2_start_all_request(pdev, pep); } else { + trace_cdns2_ep_halt(pep, 1, 0); set_reg_bit_8(conf, EPX_CON_STALL); writeb(dir | pep->num, &pdev->epx_regs->endprst); writeb(dir | ENDPRST_FIFORST | pep->num, @@ -1848,6 +1894,7 @@ static int cdns2_gadget_ep_set_halt(struct usb_ep *ep, int value) preq = cdns2_next_preq(&pep->pending_list); if (value && preq) { + trace_cdns2_ep_busy_try_halt_again(pep); ret = -EAGAIN; goto done; } @@ -2011,6 +2058,8 @@ static int cdns2_gadget_pullup(struct usb_gadget *gadget, int is_on) struct cdns2_device *pdev = gadget_to_cdns2_device(gadget); unsigned long flags; + trace_cdns2_pullup(is_on); + /* * Disable events handling while controller is being * enabled/disabled. @@ -2336,6 +2385,7 @@ int cdns2_gadget_suspend(struct cdns2_device *pdev) spin_lock_irqsave(&pdev->lock, flags); pdev->gadget.speed = USB_SPEED_UNKNOWN; + trace_cdns2_device_state("notattached"); usb_gadget_set_state(&pdev->gadget, USB_STATE_NOTATTACHED); cdns2_enable_l1(pdev, 0); diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-trace.c b/drivers/usb/gadget/udc/cdns2/cdns2-trace.c new file mode 100644 index 000000000000..de6b8cc3d071 --- /dev/null +++ b/drivers/usb/gadget/udc/cdns2/cdns2-trace.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USBHS device controller driver Trace Support + * + * Copyright (C) 2023 Cadence. + * + * Author: Pawel Laszczak + */ + +#define CREATE_TRACE_POINTS +#include "cdns2-trace.h" diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-trace.h b/drivers/usb/gadget/udc/cdns2/cdns2-trace.h new file mode 100644 index 000000000000..61f241634ea5 --- /dev/null +++ b/drivers/usb/gadget/udc/cdns2/cdns2-trace.h @@ -0,0 +1,605 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * USBHS-DEV device controller driver. + * Trace support header file. + * + * Copyright (C) 2023 Cadence. + * + * Author: Pawel Laszczak + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM cdns2-dev + +/* + * The TRACE_SYSTEM_VAR defaults to TRACE_SYSTEM, but must be a + * legitimate C variable. It is not exported to user space. + */ +#undef TRACE_SYSTEM_VAR +#define TRACE_SYSTEM_VAR cdns2_dev + +#if !defined(__LINUX_CDNS2_TRACE) || defined(TRACE_HEADER_MULTI_READ) +#define __LINUX_CDNS2_TRACE + +#include +#include +#include +#include +#include "cdns2-gadget.h" +#include "cdns2-debug.h" + +#define CDNS2_MSG_MAX 500 + +DECLARE_EVENT_CLASS(cdns2_log_enable_disable, + TP_PROTO(int set), + TP_ARGS(set), + TP_STRUCT__entry( + __field(int, set) + ), + TP_fast_assign( + __entry->set = set; + ), + TP_printk("%s", __entry->set ? "enabled" : "disabled") +); + +DEFINE_EVENT(cdns2_log_enable_disable, cdns2_pullup, + TP_PROTO(int set), + TP_ARGS(set) +); + +DEFINE_EVENT(cdns2_log_enable_disable, cdns2_lpm, + TP_PROTO(int set), + TP_ARGS(set) +); + +DEFINE_EVENT(cdns2_log_enable_disable, cdns2_may_wakeup, + TP_PROTO(int set), + TP_ARGS(set) +); + +DECLARE_EVENT_CLASS(cdns2_log_simple, + TP_PROTO(char *msg), + TP_ARGS(msg), + TP_STRUCT__entry( + __string(text, msg) + ), + TP_fast_assign( + __assign_str(text, msg); + ), + TP_printk("%s", __get_str(text)) +); + +DEFINE_EVENT(cdns2_log_simple, cdns2_no_room_on_ring, + TP_PROTO(char *msg), + TP_ARGS(msg) +); + +DEFINE_EVENT(cdns2_log_simple, cdns2_ep0_status_stage, + TP_PROTO(char *msg), + TP_ARGS(msg) +); + +DEFINE_EVENT(cdns2_log_simple, cdns2_ep0_set_config, + TP_PROTO(char *msg), + TP_ARGS(msg) +); + +DEFINE_EVENT(cdns2_log_simple, cdns2_ep0_setup, + TP_PROTO(char *msg), + TP_ARGS(msg) +); + +DEFINE_EVENT(cdns2_log_simple, cdns2_device_state, + TP_PROTO(char *msg), + TP_ARGS(msg) +); + +TRACE_EVENT(cdns2_ep_halt, + TP_PROTO(struct cdns2_endpoint *ep_priv, u8 halt, u8 flush), + TP_ARGS(ep_priv, halt, flush), + TP_STRUCT__entry( + __string(name, ep_priv->name) + __field(u8, halt) + __field(u8, flush) + ), + TP_fast_assign( + __assign_str(name, ep_priv->name); + __entry->halt = halt; + __entry->flush = flush; + ), + TP_printk("Halt %s for %s: %s", __entry->flush ? " and flush" : "", + __get_str(name), __entry->halt ? "set" : "cleared") +); + +TRACE_EVENT(cdns2_wa1, + TP_PROTO(struct cdns2_endpoint *ep_priv, char *msg), + TP_ARGS(ep_priv, msg), + TP_STRUCT__entry( + __string(ep_name, ep_priv->name) + __string(msg, msg) + ), + TP_fast_assign( + __assign_str(ep_name, ep_priv->name); + __assign_str(msg, msg); + ), + TP_printk("WA1: %s %s", __get_str(ep_name), __get_str(msg)) +); + +DECLARE_EVENT_CLASS(cdns2_log_doorbell, + TP_PROTO(struct cdns2_endpoint *pep, u32 ep_trbaddr), + TP_ARGS(pep, ep_trbaddr), + TP_STRUCT__entry( + __string(name, pep->num ? pep->name : + (pep->dir ? "ep0in" : "ep0out")) + __field(u32, ep_trbaddr) + ), + TP_fast_assign( + __assign_str(name, pep->name); + __entry->ep_trbaddr = ep_trbaddr; + ), + TP_printk("%s, ep_trbaddr %08x", __get_str(name), + __entry->ep_trbaddr) +); + +DEFINE_EVENT(cdns2_log_doorbell, cdns2_doorbell_ep0, + TP_PROTO(struct cdns2_endpoint *pep, u32 ep_trbaddr), + TP_ARGS(pep, ep_trbaddr) +); + +DEFINE_EVENT(cdns2_log_doorbell, cdns2_doorbell_epx, + TP_PROTO(struct cdns2_endpoint *pep, u32 ep_trbaddr), + TP_ARGS(pep, ep_trbaddr) +); + +DECLARE_EVENT_CLASS(cdns2_log_usb_irq, + TP_PROTO(u32 usb_irq, u32 ext_irq), + TP_ARGS(usb_irq, ext_irq), + TP_STRUCT__entry( + __field(u32, usb_irq) + __field(u32, ext_irq) + ), + TP_fast_assign( + __entry->usb_irq = usb_irq; + __entry->ext_irq = ext_irq; + ), + TP_printk("%s", cdns2_decode_usb_irq(__get_buf(CDNS2_MSG_MAX), + CDNS2_MSG_MAX, + __entry->usb_irq, + __entry->ext_irq)) +); + +DEFINE_EVENT(cdns2_log_usb_irq, cdns2_usb_irq, + TP_PROTO(u32 usb_irq, u32 ext_irq), + TP_ARGS(usb_irq, ext_irq) +); + +TRACE_EVENT(cdns2_dma_ep_ists, + TP_PROTO(u32 dma_ep_ists), + TP_ARGS(dma_ep_ists), + TP_STRUCT__entry( + __field(u32, dma_ep_ists) + ), + TP_fast_assign( + __entry->dma_ep_ists = dma_ep_ists; + ), + TP_printk("OUT: 0x%04x, IN: 0x%04x", (u16)__entry->dma_ep_ists, + __entry->dma_ep_ists >> 16) +); + +DECLARE_EVENT_CLASS(cdns2_log_epx_irq, + TP_PROTO(struct cdns2_device *pdev, struct cdns2_endpoint *pep), + TP_ARGS(pdev, pep), + TP_STRUCT__entry( + __string(ep_name, pep->name) + __field(u32, ep_sts) + __field(u32, ep_ists) + __field(u32, ep_traddr) + ), + TP_fast_assign( + __assign_str(ep_name, pep->name); + __entry->ep_sts = readl(&pdev->adma_regs->ep_sts); + __entry->ep_ists = readl(&pdev->adma_regs->ep_ists); + __entry->ep_traddr = readl(&pdev->adma_regs->ep_traddr); + ), + TP_printk("%s, ep_traddr: %08x", + cdns2_decode_epx_irq(__get_buf(CDNS2_MSG_MAX), CDNS2_MSG_MAX, + __get_str(ep_name), + __entry->ep_ists, __entry->ep_sts), + __entry->ep_traddr) +); + +DEFINE_EVENT(cdns2_log_epx_irq, cdns2_epx_irq, + TP_PROTO(struct cdns2_device *pdev, struct cdns2_endpoint *pep), + TP_ARGS(pdev, pep) +); + +DECLARE_EVENT_CLASS(cdns2_log_ep0_irq, + TP_PROTO(struct cdns2_device *pdev), + TP_ARGS(pdev), + TP_STRUCT__entry( + __field(int, ep_dir) + __field(u32, ep_ists) + __field(u32, ep_sts) + ), + TP_fast_assign( + __entry->ep_dir = pdev->selected_ep; + __entry->ep_ists = readl(&pdev->adma_regs->ep_ists); + __entry->ep_sts = readl(&pdev->adma_regs->ep_sts); + ), + TP_printk("%s", cdns2_decode_ep0_irq(__get_buf(CDNS2_MSG_MAX), + CDNS2_MSG_MAX, + __entry->ep_ists, __entry->ep_sts, + __entry->ep_dir)) +); + +DEFINE_EVENT(cdns2_log_ep0_irq, cdns2_ep0_irq, + TP_PROTO(struct cdns2_device *pdev), + TP_ARGS(pdev) +); + +DECLARE_EVENT_CLASS(cdns2_log_ctrl, + TP_PROTO(struct usb_ctrlrequest *ctrl), + TP_ARGS(ctrl), + TP_STRUCT__entry( + __field(u8, bRequestType) + __field(u8, bRequest) + __field(u16, wValue) + __field(u16, wIndex) + __field(u16, wLength) + ), + TP_fast_assign( + __entry->bRequestType = ctrl->bRequestType; + __entry->bRequest = ctrl->bRequest; + __entry->wValue = le16_to_cpu(ctrl->wValue); + __entry->wIndex = le16_to_cpu(ctrl->wIndex); + __entry->wLength = le16_to_cpu(ctrl->wLength); + ), + TP_printk("%s", usb_decode_ctrl(__get_buf(CDNS2_MSG_MAX), CDNS2_MSG_MAX, + __entry->bRequestType, + __entry->bRequest, __entry->wValue, + __entry->wIndex, __entry->wLength) + ) +); + +DEFINE_EVENT(cdns2_log_ctrl, cdns2_ctrl_req, + TP_PROTO(struct usb_ctrlrequest *ctrl), + TP_ARGS(ctrl) +); + +DECLARE_EVENT_CLASS(cdns2_log_request, + TP_PROTO(struct cdns2_request *preq), + TP_ARGS(preq), + TP_STRUCT__entry( + __string(name, preq->pep->name) + __field(struct usb_request *, request) + __field(struct cdns2_request *, preq) + __field(void *, buf) + __field(unsigned int, actual) + __field(unsigned int, length) + __field(int, status) + __field(dma_addr_t, dma) + __field(int, zero) + __field(int, short_not_ok) + __field(int, no_interrupt) + __field(struct scatterlist*, sg) + __field(unsigned int, num_sgs) + __field(unsigned int, num_mapped_sgs) + __field(int, start_trb) + __field(int, end_trb) + ), + TP_fast_assign( + __assign_str(name, preq->pep->name); + __entry->request = &preq->request; + __entry->preq = preq; + __entry->buf = preq->request.buf; + __entry->actual = preq->request.actual; + __entry->length = preq->request.length; + __entry->status = preq->request.status; + __entry->dma = preq->request.dma; + __entry->zero = preq->request.zero; + __entry->short_not_ok = preq->request.short_not_ok; + __entry->no_interrupt = preq->request.no_interrupt; + __entry->sg = preq->request.sg; + __entry->num_sgs = preq->request.num_sgs; + __entry->num_mapped_sgs = preq->request.num_mapped_sgs; + __entry->start_trb = preq->start_trb; + __entry->end_trb = preq->end_trb; + ), + TP_printk("%s: req: %p, preq: %p, req buf: %p, length: %u/%u, status: %d," + "buf dma: (%pad), %s%s%s, sg: %p, num_sgs: %d, num_m_sgs: %d," + "trb: [start: %d, end: %d]", + __get_str(name), __entry->request, __entry->preq, + __entry->buf, __entry->actual, __entry->length, + __entry->status, &__entry->dma, + __entry->zero ? "Z" : "z", + __entry->short_not_ok ? "S" : "s", + __entry->no_interrupt ? "I" : "i", + __entry->sg, __entry->num_sgs, __entry->num_mapped_sgs, + __entry->start_trb, + __entry->end_trb + ) +); + +DEFINE_EVENT(cdns2_log_request, cdns2_request_enqueue, + TP_PROTO(struct cdns2_request *preq), + TP_ARGS(preq) +); + +DEFINE_EVENT(cdns2_log_request, cdns2_request_enqueue_error, + TP_PROTO(struct cdns2_request *preq), + TP_ARGS(preq) +); + +DEFINE_EVENT(cdns2_log_request, cdns2_alloc_request, + TP_PROTO(struct cdns2_request *preq), + TP_ARGS(preq) +); + +DEFINE_EVENT(cdns2_log_request, cdns2_free_request, + TP_PROTO(struct cdns2_request *preq), + TP_ARGS(preq) +); + +DEFINE_EVENT(cdns2_log_request, cdns2_ep_queue, + TP_PROTO(struct cdns2_request *preq), + TP_ARGS(preq) +); + +DEFINE_EVENT(cdns2_log_request, cdns2_request_dequeue, + TP_PROTO(struct cdns2_request *preq), + TP_ARGS(preq) +); + +DEFINE_EVENT(cdns2_log_request, cdns2_request_giveback, + TP_PROTO(struct cdns2_request *preq), + TP_ARGS(preq) +); + +TRACE_EVENT(cdns2_ep0_enqueue, + TP_PROTO(struct cdns2_device *dev_priv, struct usb_request *request), + TP_ARGS(dev_priv, request), + TP_STRUCT__entry( + __field(int, dir) + __field(int, length) + ), + TP_fast_assign( + __entry->dir = dev_priv->eps[0].dir; + __entry->length = request->length; + ), + TP_printk("Queue to ep0%s length: %u", __entry->dir ? "in" : "out", + __entry->length) +); + +DECLARE_EVENT_CLASS(cdns2_log_map_request, + TP_PROTO(struct cdns2_request *priv_req), + TP_ARGS(priv_req), + TP_STRUCT__entry( + __string(name, priv_req->pep->name) + __field(struct usb_request *, req) + __field(void *, buf) + __field(dma_addr_t, dma) + ), + TP_fast_assign( + __assign_str(name, priv_req->pep->name); + __entry->req = &priv_req->request; + __entry->buf = priv_req->request.buf; + __entry->dma = priv_req->request.dma; + ), + TP_printk("%s: req: %p, req buf %p, dma %p", + __get_str(name), __entry->req, __entry->buf, &__entry->dma + ) +); + +DEFINE_EVENT(cdns2_log_map_request, cdns2_map_request, + TP_PROTO(struct cdns2_request *req), + TP_ARGS(req) +); +DEFINE_EVENT(cdns2_log_map_request, cdns2_mapped_request, + TP_PROTO(struct cdns2_request *req), + TP_ARGS(req) +); + +DECLARE_EVENT_CLASS(cdns2_log_trb, + TP_PROTO(struct cdns2_endpoint *pep, struct cdns2_trb *trb), + TP_ARGS(pep, trb), + TP_STRUCT__entry( + __string(name, pep->name) + __field(struct cdns2_trb *, trb) + __field(u32, buffer) + __field(u32, length) + __field(u32, control) + __field(u32, type) + ), + TP_fast_assign( + __assign_str(name, pep->name); + __entry->trb = trb; + __entry->buffer = le32_to_cpu(trb->buffer); + __entry->length = le32_to_cpu(trb->length); + __entry->control = le32_to_cpu(trb->control); + __entry->type = usb_endpoint_type(pep->endpoint.desc); + ), + TP_printk("%s: trb V: %p, dma buf: P: 0x%08x, %s", + __get_str(name), __entry->trb, __entry->buffer, + cdns2_decode_trb(__get_buf(CDNS2_MSG_MAX), CDNS2_MSG_MAX, + __entry->control, __entry->length, + __entry->buffer)) +); + +DEFINE_EVENT(cdns2_log_trb, cdns2_queue_trb, + TP_PROTO(struct cdns2_endpoint *pep, struct cdns2_trb *trb), + TP_ARGS(pep, trb) +); + +DEFINE_EVENT(cdns2_log_trb, cdns2_complete_trb, + TP_PROTO(struct cdns2_endpoint *pep, struct cdns2_trb *trb), + TP_ARGS(pep, trb) +); + +DECLARE_EVENT_CLASS(cdns2_log_ring, + TP_PROTO(struct cdns2_endpoint *pep), + TP_ARGS(pep), + TP_STRUCT__entry( + __dynamic_array(u8, tr_seg, TR_SEG_SIZE) + __dynamic_array(u8, pep, sizeof(struct cdns2_endpoint)) + __dynamic_array(char, buffer, + (TRBS_PER_SEGMENT * 65) + CDNS2_MSG_MAX) + ), + TP_fast_assign( + memcpy(__get_dynamic_array(pep), pep, + sizeof(struct cdns2_endpoint)); + memcpy(__get_dynamic_array(tr_seg), pep->ring.trbs, + TR_SEG_SIZE); + ), + + TP_printk("%s", + cdns2_raw_ring((struct cdns2_endpoint *)__get_str(pep), + (struct cdns2_trb *)__get_str(tr_seg), + __get_str(buffer), + (TRBS_PER_SEGMENT * 65) + CDNS2_MSG_MAX)) +); + +DEFINE_EVENT(cdns2_log_ring, cdns2_ring, + TP_PROTO(struct cdns2_endpoint *pep), + TP_ARGS(pep) +); + +DECLARE_EVENT_CLASS(cdns2_log_ep, + TP_PROTO(struct cdns2_endpoint *pep), + TP_ARGS(pep), + TP_STRUCT__entry( + __string(name, pep->name) + __field(unsigned int, maxpacket) + __field(unsigned int, maxpacket_limit) + __field(unsigned int, flags) + __field(unsigned int, dir) + __field(u8, enqueue) + __field(u8, dequeue) + ), + TP_fast_assign( + __assign_str(name, pep->name); + __entry->maxpacket = pep->endpoint.maxpacket; + __entry->maxpacket_limit = pep->endpoint.maxpacket_limit; + __entry->flags = pep->ep_state; + __entry->dir = pep->dir; + __entry->enqueue = pep->ring.enqueue; + __entry->dequeue = pep->ring.dequeue; + ), + TP_printk("%s: mps: %d/%d, enq idx: %d, deq idx: %d, " + "flags: %s%s%s%s, dir: %s", + __get_str(name), __entry->maxpacket, + __entry->maxpacket_limit, __entry->enqueue, + __entry->dequeue, + __entry->flags & EP_ENABLED ? "EN | " : "", + __entry->flags & EP_STALLED ? "STALLED | " : "", + __entry->flags & EP_WEDGE ? "WEDGE | " : "", + __entry->flags & EP_RING_FULL ? "RING FULL |" : "", + __entry->dir ? "IN" : "OUT" + ) +); + +DEFINE_EVENT(cdns2_log_ep, cdns2_gadget_ep_enable, + TP_PROTO(struct cdns2_endpoint *pep), + TP_ARGS(pep) +); + +DEFINE_EVENT(cdns2_log_ep, cdns2_gadget_ep_disable, + TP_PROTO(struct cdns2_endpoint *pep), + TP_ARGS(pep) +); + +DEFINE_EVENT(cdns2_log_ep, cdns2_iso_out_ep_disable, + TP_PROTO(struct cdns2_endpoint *pep), + TP_ARGS(pep) +); + +DEFINE_EVENT(cdns2_log_ep, cdns2_ep_busy_try_halt_again, + TP_PROTO(struct cdns2_endpoint *pep), + TP_ARGS(pep) +); + +DECLARE_EVENT_CLASS(cdns2_log_request_handled, + TP_PROTO(struct cdns2_request *priv_req, int current_index, + int handled), + TP_ARGS(priv_req, current_index, handled), + TP_STRUCT__entry( + __field(struct cdns2_request *, priv_req) + __field(unsigned int, dma_position) + __field(unsigned int, handled) + __field(unsigned int, dequeue_idx) + __field(unsigned int, enqueue_idx) + __field(unsigned int, start_trb) + __field(unsigned int, end_trb) + ), + TP_fast_assign( + __entry->priv_req = priv_req; + __entry->dma_position = current_index; + __entry->handled = handled; + __entry->dequeue_idx = priv_req->pep->ring.dequeue; + __entry->enqueue_idx = priv_req->pep->ring.enqueue; + __entry->start_trb = priv_req->start_trb; + __entry->end_trb = priv_req->end_trb; + ), + TP_printk("Req: %p %s, DMA pos: %d, ep deq: %d, ep enq: %d," + " start trb: %d, end trb: %d", + __entry->priv_req, + __entry->handled ? "handled" : "not handled", + __entry->dma_position, __entry->dequeue_idx, + __entry->enqueue_idx, __entry->start_trb, + __entry->end_trb + ) +); + +DEFINE_EVENT(cdns2_log_request_handled, cdns2_request_handled, + TP_PROTO(struct cdns2_request *priv_req, int current_index, + int handled), + TP_ARGS(priv_req, current_index, handled) +); + +DECLARE_EVENT_CLASS(cdns2_log_epx_reg_config, + TP_PROTO(struct cdns2_device *pdev, struct cdns2_endpoint *pep), + TP_ARGS(pdev, pep), + TP_STRUCT__entry( + __string(ep_name, pep->name) + __field(u8, burst_size) + __field(__le16, maxpack_reg) + __field(__u8, con_reg) + __field(u32, ep_sel_reg) + __field(u32, ep_sts_en_reg) + __field(u32, ep_cfg_reg) + ), + TP_fast_assign( + __assign_str(ep_name, pep->name); + __entry->burst_size = pep->trb_burst_size; + __entry->maxpack_reg = pep->dir ? readw(&pdev->epx_regs->txmaxpack[pep->num - 1]) : + readw(&pdev->epx_regs->rxmaxpack[pep->num - 1]); + __entry->con_reg = pep->dir ? readb(&pdev->epx_regs->ep[pep->num - 1].txcon) : + readb(&pdev->epx_regs->ep[pep->num - 1].rxcon); + __entry->ep_sel_reg = readl(&pdev->adma_regs->ep_sel); + __entry->ep_sts_en_reg = readl(&pdev->adma_regs->ep_sts_en); + __entry->ep_cfg_reg = readl(&pdev->adma_regs->ep_cfg); + ), + + TP_printk("%s, maxpack: %d, con: %02x, dma_ep_sel: %08x, dma_ep_sts_en: %08x" + " dma_ep_cfg %08x", + __get_str(ep_name), __entry->maxpack_reg, __entry->con_reg, + __entry->ep_sel_reg, __entry->ep_sts_en_reg, + __entry->ep_cfg_reg + ) +); + +DEFINE_EVENT(cdns2_log_epx_reg_config, cdns2_epx_hw_cfg, + TP_PROTO(struct cdns2_device *pdev, struct cdns2_endpoint *pep), + TP_ARGS(pdev, pep) +); + +#endif /* __LINUX_CDNS2_TRACE */ + +/* This part must be outside header guard. */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE cdns2-trace + +#include -- cgit v1.2.3 From 41e2f976b558ca71fb79dbc7874c4fc91370b5d6 Mon Sep 17 00:00:00 2001 From: Pawel Laszczak Date: Fri, 2 Jun 2023 06:26:44 -0400 Subject: MAINTAINERS: add Cadence USBHS driver entry Patch adds entry for USBHS (CDNS2) driver into MAINTARNERS file Signed-off-by: Pawel Laszczak Message-ID: <20230602102644.77470-5-pawell@cadence.com> Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6fe915cdbf84..a8c8bd0b845f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4520,6 +4520,12 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git F: drivers/usb/cdns3/ X: drivers/usb/cdns3/cdns3* +CADENCE USBHS DRIVER +M: Pawel Laszczak +L: linux-usb@vger.kernel.org +S: Maintained +F: drivers/usb/gadget/udc/cdns2 + CADET FM/AM RADIO RECEIVER DRIVER M: Hans Verkuil L: linux-media@vger.kernel.org -- cgit v1.2.3 From 0ac37fbdad7087bbcbbe246a602c248ccfd954ea Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Wed, 17 May 2023 16:19:06 +0800 Subject: usb: chipidea: imx: turn off vbus comparator when suspend As we use bvalid for vbus wakeup source, to save power when suspend, turn off the vbus comparator for imx7d and imx8mm. Below is this bit description from RM of iMX8MM "VBUS Valid Comparator Enable: This signal controls the USB OTG PHY VBUS Valid comparator which indicates whether the voltage on the USB_OTG*_VBUS pin is below the VBUS Valid threshold. The VBUS Valid threshold is nominally 4.75V on this USB PHY. The VBUS Valid threshold can be adjusted using the USBNC_OTGn_PHY_CFG1[OTGTUNE0] bit field. Status of the VBUS Valid comparator, when it is enabled, is reported on the USBNC_OTGn_PHY_STATUS[VBUS_VLD] bit. When OTGDISABLE0 (USBNC_USB_OTGx_PHY_CFG2[10])is set to 1'b0 and DRVVBUS0 is set to 1'b1, the Bandgap circuitry and VBUS Valid comparator are powered, even in Suspend or Sleep mode. DRVVBUS0 should be reset to 1'b0 when the internal VBUS Valid comparator is not required, to reduce quiescent current in Suspend or Sleep mode. - 0 The VBUS Valid comparator is disabled - 1 The VBUS Valid comparator is enabled" Signed-off-by: Li Jun Signed-off-by: Xu Yang Acked-by: Peter Chen Message-ID: <20230517081907.3410465-2-xu.yang_2@nxp.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/usbmisc_imx.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index be939e77719d..9566022a2183 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -151,6 +151,7 @@ struct usbmisc_ops { int (*charger_detection)(struct imx_usbmisc_data *data); /* It's called when system resume from usb power lost */ int (*power_lost_check)(struct imx_usbmisc_data *data); + void (*vbus_comparator_on)(struct imx_usbmisc_data *data, bool on); }; struct imx_usbmisc { @@ -874,6 +875,33 @@ static int imx7d_charger_detection(struct imx_usbmisc_data *data) return ret; } +static void usbmisc_imx7d_vbus_comparator_on(struct imx_usbmisc_data *data, + bool on) +{ + unsigned long flags; + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + u32 val; + + if (data->hsic) + return; + + spin_lock_irqsave(&usbmisc->lock, flags); + /* + * Disable VBUS valid comparator when in suspend mode, + * when OTG is disabled and DRVVBUS0 is asserted case + * the Bandgap circuitry and VBUS Valid comparator are + * still powered, even in Suspend or Sleep mode. + */ + val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + if (on) + val |= MX7D_USB_OTG_PHY_CFG2_DRVVBUS0; + else + val &= ~MX7D_USB_OTG_PHY_CFG2_DRVVBUS0; + + writel(val, usbmisc->base + MX7D_USB_OTG_PHY_CFG2); + spin_unlock_irqrestore(&usbmisc->lock, flags); +} + static int usbmisc_imx7ulp_init(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); @@ -1017,6 +1045,7 @@ static const struct usbmisc_ops imx7d_usbmisc_ops = { .set_wakeup = usbmisc_imx7d_set_wakeup, .charger_detection = imx7d_charger_detection, .power_lost_check = usbmisc_imx7d_power_lost_check, + .vbus_comparator_on = usbmisc_imx7d_vbus_comparator_on, }; static const struct usbmisc_ops imx7ulp_usbmisc_ops = { @@ -1131,6 +1160,9 @@ int imx_usbmisc_suspend(struct imx_usbmisc_data *data, bool wakeup) usbmisc = dev_get_drvdata(data->dev); + if (usbmisc->ops->vbus_comparator_on) + usbmisc->ops->vbus_comparator_on(data, false); + if (wakeup && usbmisc->ops->set_wakeup) ret = usbmisc->ops->set_wakeup(data, true); if (ret) { @@ -1184,6 +1216,9 @@ int imx_usbmisc_resume(struct imx_usbmisc_data *data, bool wakeup) goto hsic_set_clk_fail; } + if (usbmisc->ops->vbus_comparator_on) + usbmisc->ops->vbus_comparator_on(data, true); + return 0; hsic_set_clk_fail: -- cgit v1.2.3 From 53d061c19dc4cb68409df6dc11c40389c8c42a75 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Wed, 17 May 2023 16:19:07 +0800 Subject: usb: chipidea: imx: add missing USB PHY DPDM wakeup setting USB PHY DPDM wakeup bit is enabled by default, when USB wakeup is not required(/sys/.../wakeup is disabled), this bit should be disabled, otherwise we will have unexpected wakeup if do USB device connect/disconnect while system sleep. This bit can be enabled for both host and device mode. Signed-off-by: Li Jun Signed-off-by: Xu Yang Acked-by: Peter Chen Message-ID: <20230517081907.3410465-3-xu.yang_2@nxp.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/chipidea/usbmisc_imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 9566022a2183..9ee9621e2ccc 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -134,7 +134,7 @@ #define TXVREFTUNE0_MASK (0xf << 20) #define MX6_USB_OTG_WAKEUP_BITS (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | \ - MX6_BM_ID_WAKEUP) + MX6_BM_ID_WAKEUP | MX6SX_BM_DPDM_WAKEUP_EN) struct usbmisc_ops { /* It's called once when probe a usb device */ -- cgit v1.2.3 From df49f2a0ac4a34c0cb4b5c233fcfa0add644c43c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 15 Jun 2023 11:30:35 +0200 Subject: Revert "usb: common: usb-conn-gpio: Set last role to unknown before initial detection" This reverts commit edd60d24bd858cef165274e4cd6cab43bdc58d15. Heikki reports that this should not be a global flag just to work around one broken driver and should be fixed differently, so revert it. Reported-by: Heikki Krogerus Fixes: edd60d24bd85 ("usb: common: usb-conn-gpio: Set last role to unknown before initial detection") Link: https://lore.kernel.org/r/ZImE4L3YgABnCIsP@kuha.fi.intel.com Cc: Prashanth K Cc: AngeloGioacchino Del Regno Signed-off-by: Greg Kroah-Hartman --- drivers/usb/cdns3/core.c | 2 -- drivers/usb/common/usb-conn-gpio.c | 3 --- drivers/usb/musb/jz4740.c | 2 -- drivers/usb/roles/intel-xhci-usb-role-switch.c | 2 -- include/linux/usb/role.h | 1 - 5 files changed, 10 deletions(-) diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 69d2921f2d3b..dbcdf3b24b47 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -252,8 +252,6 @@ static enum usb_role cdns_hw_role_state_machine(struct cdns *cdns) if (!vbus) role = USB_ROLE_NONE; break; - default: - break; } dev_dbg(cdns->dev, "role %d -> %d\n", cdns->role, role); diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c index d0e8624c7dfe..766005d20bae 100644 --- a/drivers/usb/common/usb-conn-gpio.c +++ b/drivers/usb/common/usb-conn-gpio.c @@ -257,9 +257,6 @@ static int usb_conn_probe(struct platform_device *pdev) platform_set_drvdata(pdev, info); device_set_wakeup_capable(&pdev->dev, true); - /* Set last role to unknown before performing the initial detection */ - info->last_role = USB_ROLE_UNKNOWN; - /* Perform initial detection */ usb_conn_queue_dwork(info, 0); diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index 6d880c4cce36..5aabdd7e2511 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -95,8 +95,6 @@ static int jz4740_musb_role_switch_set(struct usb_role_switch *sw, case USB_ROLE_HOST: atomic_notifier_call_chain(&phy->notifier, USB_EVENT_ID, phy); break; - default: - break; } return 0; diff --git a/drivers/usb/roles/intel-xhci-usb-role-switch.c b/drivers/usb/roles/intel-xhci-usb-role-switch.c index 8e2997d65f11..e5c6c413a075 100644 --- a/drivers/usb/roles/intel-xhci-usb-role-switch.c +++ b/drivers/usb/roles/intel-xhci-usb-role-switch.c @@ -97,8 +97,6 @@ static int intel_xhci_usb_set_role(struct usb_role_switch *sw, val |= SW_VBUS_VALID; drd_config = DRD_CONFIG_STATIC_DEVICE; break; - default: - break; } val |= SW_IDPIN_EN; if (data->enable_sw_switch) { diff --git a/include/linux/usb/role.h b/include/linux/usb/role.h index 65e790a28913..b5deafd91f67 100644 --- a/include/linux/usb/role.h +++ b/include/linux/usb/role.h @@ -11,7 +11,6 @@ enum usb_role { USB_ROLE_NONE, USB_ROLE_HOST, USB_ROLE_DEVICE, - USB_ROLE_UNKNOWN, }; typedef int (*usb_role_switch_set_t)(struct usb_role_switch *sw, -- cgit v1.2.3 From 09b69dd4378b91b3ac3fbac387fb992dc21c0f88 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 14 Jun 2023 11:13:11 -0700 Subject: usb: ch9: Replace 1-element array with flexible array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit df8fc4e934c1 ("kbuild: Enable -fstrict-flex-arrays=3"), UBSAN_BOUNDS no longer pretends 1-element arrays are unbounded. Walking wData will trigger a warning, so make it a proper flexible array. Add a union to keep the struct size identical for userspace in case anything was depending on the old size. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-lkp/202306102333.8f5a7443-oliver.sang@intel.com Cc: Greg Kroah-Hartman Cc: kernel test robot Cc: "Gustavo A. R. Silva" Cc: Dan Williams Cc: "Jó Ágila Bitsch" Signed-off-by: Kees Cook Message-ID: <20230614181307.gonna.256-kees@kernel.org> Signed-off-by: Greg Kroah-Hartman --- include/uapi/linux/usb/ch9.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index b17e3a21b15f..82ec6af71a1d 100644 --- a/include/uapi/linux/usb/ch9.h +++ b/include/uapi/linux/usb/ch9.h @@ -376,7 +376,10 @@ struct usb_string_descriptor { __u8 bLength; __u8 bDescriptorType; - __le16 wData[1]; /* UTF-16LE encoded */ + union { + __le16 legacy_padding; + __DECLARE_FLEX_ARRAY(__le16, wData); /* UTF-16LE encoded */ + }; } __attribute__ ((packed)); /* note that "string" zero is special, it holds language codes that -- cgit v1.2.3 From 771e0e37bff042b31e8b104263c7ebbe915a0fd3 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 13 Jun 2023 16:50:07 +0200 Subject: dt-bindings: usb: add ON Semiconductor nb7vpq904m Type-C Linear Redriver bindings Document bindings for this ON Semiconductor Type-C USB SuperSpeed and DisplayPort ALT Mode Linear Redriver. Signed-off-by: Neil Armstrong Reviewed-by: Krzysztof Kozlowski Message-ID: <20230601-topic-sm8x50-upstream-redriver-v3-1-988c560e2195@linaro.org> Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/usb/onnn,nb7vpq904m.yaml | 141 +++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/onnn,nb7vpq904m.yaml diff --git a/Documentation/devicetree/bindings/usb/onnn,nb7vpq904m.yaml b/Documentation/devicetree/bindings/usb/onnn,nb7vpq904m.yaml new file mode 100644 index 000000000000..c0201da002f6 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/onnn,nb7vpq904m.yaml @@ -0,0 +1,141 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/onnn,nb7vpq904m.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ON Semiconductor Type-C DisplayPort ALT Mode Linear Redriver + +maintainers: + - Neil Armstrong + +properties: + compatible: + enum: + - onnn,nb7vpq904m + + reg: + maxItems: 1 + + vcc-supply: + description: power supply (1.8V) + + enable-gpios: true + + retimer-switch: + description: Flag the port as possible handle of SuperSpeed signals retiming + type: boolean + + orientation-switch: + description: Flag the port as possible handler of orientation switching + type: boolean + + ports: + $ref: /schemas/graph.yaml#/properties/ports + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: Super Speed (SS) Output endpoint to the Type-C connector + + port@1: + $ref: /schemas/graph.yaml#/$defs/port-base + description: Super Speed (SS) Input endpoint from the Super-Speed PHY + unevaluatedProperties: false + + properties: + endpoint: + $ref: /schemas/graph.yaml#/$defs/endpoint-base + unevaluatedProperties: false + + properties: + data-lanes: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: | + An array of physical data lane indexes. Position determines how + lanes are connected to the redriver, It is assumed the same order + is kept on the other side of the redriver. + Lane number represents the following + - 0 is RX2 lane + - 1 is TX2 lane + - 2 is TX1 lane + - 3 is RX1 lane + The position determines the physical port of the redriver, in the + order A, B, C & D. + oneOf: + - items: + - const: 0 + - const: 1 + - const: 2 + - const: 3 + description: | + This is the lanes default layout + - Port A to RX2 lane + - Port B to TX2 lane + - Port C to TX1 lane + - Port D to RX1 lane + - items: + - const: 3 + - const: 2 + - const: 1 + - const: 0 + description: | + This is the USBRX2/USBTX2 and USBRX1/USBTX1 swapped lanes layout + - Port A to RX1 lane + - Port B to TX1 lane + - Port C to TX2 lane + - Port D to RX2 lane + + port@2: + $ref: /schemas/graph.yaml#/properties/port + description: + Sideband Use (SBU) AUX lines endpoint to the Type-C connector for the purpose of + handling altmode muxing and orientation switching. + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + typec-mux@32 { + compatible = "onnn,nb7vpq904m"; + reg = <0x32>; + + vcc-supply = <&vreg_l15b_1p8>; + + retimer-switch; + orientation-switch; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + usb_con_ss: endpoint { + remote-endpoint = <&typec_con_ss>; + }; + }; + port@1 { + reg = <1>; + phy_con_ss: endpoint { + remote-endpoint = <&usb_phy_ss>; + data-lanes = <3 2 1 0>; + }; + }; + port@2 { + reg = <2>; + usb_con_sbu: endpoint { + remote-endpoint = <&typec_dp_aux>; + }; + }; + }; + }; + }; +... -- cgit v1.2.3 From c43e7983fcc3f7dea0b49adfae848c2830d80a59 Mon Sep 17 00:00:00 2001 From: Piyush Mehta Date: Tue, 13 Jun 2023 18:00:48 +0530 Subject: dt-bindings: reset: convert the xlnx,zynqmp-reset.txt to yaml Convert the binding to DT schema format. It also updates the reset-controller description. Signed-off-by: Piyush Mehta Reviewed-by: Krzysztof Kozlowski Message-ID: <20230613123048.2935502-1-piyush.mehta@amd.com> Signed-off-by: Greg Kroah-Hartman --- .../bindings/reset/xlnx,zynqmp-reset.txt | 55 ---------------------- .../bindings/reset/xlnx,zynqmp-reset.yaml | 52 ++++++++++++++++++++ 2 files changed, 52 insertions(+), 55 deletions(-) delete mode 100644 Documentation/devicetree/bindings/reset/xlnx,zynqmp-reset.txt create mode 100644 Documentation/devicetree/bindings/reset/xlnx,zynqmp-reset.yaml diff --git a/Documentation/devicetree/bindings/reset/xlnx,zynqmp-reset.txt b/Documentation/devicetree/bindings/reset/xlnx,zynqmp-reset.txt deleted file mode 100644 index ed836868dbf1..000000000000 --- a/Documentation/devicetree/bindings/reset/xlnx,zynqmp-reset.txt +++ /dev/null @@ -1,55 +0,0 @@ --------------------------------------------------------------------------- - = Zynq UltraScale+ MPSoC and Versal reset driver binding = --------------------------------------------------------------------------- -The Zynq UltraScale+ MPSoC and Versal has several different resets. - -See Chapter 36 of the Zynq UltraScale+ MPSoC TRM (UG) for more information -about zynqmp resets. - -Please also refer to reset.txt in this directory for common reset -controller binding usage. - -Required Properties: -- compatible: "xlnx,zynqmp-reset" for Zynq UltraScale+ MPSoC platform - "xlnx,versal-reset" for Versal platform -- #reset-cells: Specifies the number of cells needed to encode reset - line, should be 1 - -------- -Example -------- - -firmware { - zynqmp_firmware: zynqmp-firmware { - compatible = "xlnx,zynqmp-firmware"; - method = "smc"; - - zynqmp_reset: reset-controller { - compatible = "xlnx,zynqmp-reset"; - #reset-cells = <1>; - }; - }; -}; - -Specifying reset lines connected to IP modules -============================================== - -Device nodes that need access to reset lines should -specify them as a reset phandle in their corresponding node as -specified in reset.txt. - -For list of all valid reset indices for Zynq UltraScale+ MPSoC see - -For list of all valid reset indices for Versal see - - -Example: - -serdes: zynqmp_phy@fd400000 { - ... - - resets = <&zynqmp_reset ZYNQMP_RESET_SATA>; - reset-names = "sata_rst"; - - ... -}; diff --git a/Documentation/devicetree/bindings/reset/xlnx,zynqmp-reset.yaml b/Documentation/devicetree/bindings/reset/xlnx,zynqmp-reset.yaml new file mode 100644 index 000000000000..0d50f6a54af3 --- /dev/null +++ b/Documentation/devicetree/bindings/reset/xlnx,zynqmp-reset.yaml @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/reset/xlnx,zynqmp-reset.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Zynq UltraScale+ MPSoC and Versal reset + +maintainers: + - Piyush Mehta + +description: | + The Zynq UltraScale+ MPSoC and Versal has several different resets. + + The PS reset subsystem is responsible for handling the external reset + input to the device and that all internal reset requirements are met + for the system (as a whole) and for the functional units. + + Please also refer to reset.txt in this directory for common reset + controller binding usage. Device nodes that need access to reset + lines should specify them as a reset phandle in their corresponding + node as specified in reset.txt. + + For list of all valid reset indices for Zynq UltraScale+ MPSoC + + + For list of all valid reset indices for Versal + + +properties: + compatible: + enum: + - xlnx,zynqmp-reset + - xlnx,versal-reset + + "#reset-cells": + const: 1 + +required: + - compatible + - "#reset-cells" + +additionalProperties: false + +examples: + - | + zynqmp_reset: reset-controller { + compatible = "xlnx,zynqmp-reset"; + #reset-cells = <1>; + }; + +... -- cgit v1.2.3 From 4ee94d9407898a5c0d3824d50ec91e4cb1276761 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 15 Jun 2023 11:43:07 +0200 Subject: MAINTAINERS: USB: add uapi header files to USB SUBSYSTEM entry For some reason the include/uapi/linux/usb/ directory is missing in the USB SUBSYSTEM entry, so get_maintainer will not know to cc: the proper mailing lists. Fix this up by adding an entry for this directory. Message-ID: <20230615094306.2072827-2-gregkh@linuxfoundation.org> Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index a8c8bd0b845f..8d8cb2067451 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21941,6 +21941,7 @@ F: drivers/usb/ F: include/dt-bindings/usb/ F: include/linux/usb.h F: include/linux/usb/ +F: include/uapi/linux/usb/ USB TYPEC BUS FOR ALTERNATE MODES M: Heikki Krogerus -- cgit v1.2.3 From 88d8f3ac9c67e2d00db671dbb0af50efb7c358cb Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 15 Jun 2023 12:32:55 +0200 Subject: usb: typec: add support for the nb7vpq904m Type-C Linear Redriver Add support for the ON Semiconductor NB7VPQ904M Type-C USB SuperSpeed and DisplayPort ALT Mode Linear Redriver chip found on some devices with a Type-C port. The redriver compensates ultra High-Speeed DisplayPort and USB Super Speed signal integrity losses mainly due to PCB & transmission cables. The redriver doesn't support SuperSpeed lines swapping, but can support Type-C SBU lines swapping. Signed-off-by: Dmitry Baryshkov Reviewed-by: Heikki Krogerus Signed-off-by: Neil Armstrong Message-ID: <20230601-topic-sm8x50-upstream-redriver-v4-2-91d5820f3a03@linaro.org> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/Kconfig | 8 + drivers/usb/typec/mux/Makefile | 1 + drivers/usb/typec/mux/nb7vpq904m.c | 529 +++++++++++++++++++++++++++++++++++++ 3 files changed, 538 insertions(+) create mode 100644 drivers/usb/typec/mux/nb7vpq904m.c diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig index c46fa4f9d3df..8c4d6b8fb75c 100644 --- a/drivers/usb/typec/mux/Kconfig +++ b/drivers/usb/typec/mux/Kconfig @@ -35,4 +35,12 @@ config TYPEC_MUX_INTEL_PMC control the USB role switch and also the multiplexer/demultiplexer switches used with USB Type-C Alternate Modes. +config TYPEC_MUX_NB7VPQ904M + tristate "On Semiconductor NB7VPQ904M Type-C redriver driver" + depends on I2C + select REGMAP_I2C + help + Say Y or M if your system has a On Semiconductor NB7VPQ904M Type-C + redriver chip found on some devices with a Type-C port. + endmenu diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile index dda67e19b58b..76196096ef41 100644 --- a/drivers/usb/typec/mux/Makefile +++ b/drivers/usb/typec/mux/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_TYPEC_MUX_FSA4480) += fsa4480.o obj-$(CONFIG_TYPEC_MUX_GPIO_SBU) += gpio-sbu-mux.o obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o obj-$(CONFIG_TYPEC_MUX_INTEL_PMC) += intel_pmc_mux.o +obj-$(CONFIG_TYPEC_MUX_NB7VPQ904M) += nb7vpq904m.o diff --git a/drivers/usb/typec/mux/nb7vpq904m.c b/drivers/usb/typec/mux/nb7vpq904m.c new file mode 100644 index 000000000000..80e580d50129 --- /dev/null +++ b/drivers/usb/typec/mux/nb7vpq904m.c @@ -0,0 +1,529 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * OnSemi NB7VPQ904M Type-C driver + * + * Copyright (C) 2023 Dmitry Baryshkov + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NB7_CHNA 0 +#define NB7_CHNB 1 +#define NB7_CHNC 2 +#define NB7_CHND 3 +#define NB7_IS_CHAN_AD(channel) (channel == NB7_CHNA || channel == NB7_CHND) + +#define GEN_DEV_SET_REG 0x00 + +#define GEN_DEV_SET_CHIP_EN BIT(0) +#define GEN_DEV_SET_CHNA_EN BIT(4) +#define GEN_DEV_SET_CHNB_EN BIT(5) +#define GEN_DEV_SET_CHNC_EN BIT(6) +#define GEN_DEV_SET_CHND_EN BIT(7) + +#define GEN_DEV_SET_OP_MODE_MASK GENMASK(3, 1) + +#define GEN_DEV_SET_OP_MODE_DP_CC2 0 +#define GEN_DEV_SET_OP_MODE_DP_CC1 1 +#define GEN_DEV_SET_OP_MODE_DP_4LANE 2 +#define GEN_DEV_SET_OP_MODE_USB 5 + +#define EQ_SETTING_REG_BASE 0x01 +#define EQ_SETTING_REG(n) (EQ_SETTING_REG_BASE + (n) * 2) +#define EQ_SETTING_MASK GENMASK(3, 1) + +#define OUTPUT_COMPRESSION_AND_POL_REG_BASE 0x02 +#define OUTPUT_COMPRESSION_AND_POL_REG(n) (OUTPUT_COMPRESSION_AND_POL_REG_BASE + (n) * 2) +#define OUTPUT_COMPRESSION_MASK GENMASK(2, 1) + +#define FLAT_GAIN_REG_BASE 0x18 +#define FLAT_GAIN_REG(n) (FLAT_GAIN_REG_BASE + (n) * 2) +#define FLAT_GAIN_MASK GENMASK(1, 0) + +#define LOSS_MATCH_REG_BASE 0x19 +#define LOSS_MATCH_REG(n) (LOSS_MATCH_REG_BASE + (n) * 2) +#define LOSS_MATCH_MASK GENMASK(1, 0) + +#define AUX_CC_REG 0x09 + +#define CHIP_VERSION_REG 0x17 + +struct nb7vpq904m { + struct i2c_client *client; + struct gpio_desc *enable_gpio; + struct regulator *vcc_supply; + struct regmap *regmap; + struct typec_switch_dev *sw; + struct typec_retimer *retimer; + + bool swap_data_lanes; + struct typec_switch *typec_switch; + + struct drm_bridge bridge; + + struct mutex lock; /* protect non-concurrent retimer & switch */ + + enum typec_orientation orientation; + unsigned long mode; + unsigned int svid; +}; + +static void nb7vpq904m_set_channel(struct nb7vpq904m *nb7, unsigned int channel, bool dp) +{ + u8 eq, out_comp, flat_gain, loss_match; + + if (dp) { + eq = NB7_IS_CHAN_AD(channel) ? 0x6 : 0x4; + out_comp = 0x3; + flat_gain = NB7_IS_CHAN_AD(channel) ? 0x2 : 0x1; + loss_match = 0x3; + } else { + eq = 0x4; + out_comp = 0x3; + flat_gain = NB7_IS_CHAN_AD(channel) ? 0x3 : 0x1; + loss_match = NB7_IS_CHAN_AD(channel) ? 0x1 : 0x3; + } + + regmap_update_bits(nb7->regmap, EQ_SETTING_REG(channel), + EQ_SETTING_MASK, FIELD_PREP(EQ_SETTING_MASK, eq)); + regmap_update_bits(nb7->regmap, OUTPUT_COMPRESSION_AND_POL_REG(channel), + OUTPUT_COMPRESSION_MASK, FIELD_PREP(OUTPUT_COMPRESSION_MASK, out_comp)); + regmap_update_bits(nb7->regmap, FLAT_GAIN_REG(channel), + FLAT_GAIN_MASK, FIELD_PREP(FLAT_GAIN_MASK, flat_gain)); + regmap_update_bits(nb7->regmap, LOSS_MATCH_REG(channel), + LOSS_MATCH_MASK, FIELD_PREP(LOSS_MATCH_MASK, loss_match)); +} + +static int nb7vpq904m_set(struct nb7vpq904m *nb7) +{ + bool reverse = (nb7->orientation == TYPEC_ORIENTATION_REVERSE); + + switch (nb7->mode) { + case TYPEC_STATE_SAFE: + regmap_write(nb7->regmap, GEN_DEV_SET_REG, + GEN_DEV_SET_CHIP_EN | + GEN_DEV_SET_CHNA_EN | + GEN_DEV_SET_CHNB_EN | + GEN_DEV_SET_CHNC_EN | + GEN_DEV_SET_CHND_EN | + FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK, + GEN_DEV_SET_OP_MODE_USB)); + nb7vpq904m_set_channel(nb7, NB7_CHNA, false); + nb7vpq904m_set_channel(nb7, NB7_CHNB, false); + nb7vpq904m_set_channel(nb7, NB7_CHNC, false); + nb7vpq904m_set_channel(nb7, NB7_CHND, false); + regmap_write(nb7->regmap, AUX_CC_REG, 0x2); + + return 0; + + case TYPEC_STATE_USB: + /* + * Normal Orientation (CC1) + * A -> USB RX + * B -> USB TX + * C -> X + * D -> X + * Flipped Orientation (CC2) + * A -> X + * B -> X + * C -> USB TX + * D -> USB RX + * + * Reversed if data lanes are swapped + */ + if (reverse ^ nb7->swap_data_lanes) { + regmap_write(nb7->regmap, GEN_DEV_SET_REG, + GEN_DEV_SET_CHIP_EN | + GEN_DEV_SET_CHNA_EN | + GEN_DEV_SET_CHNB_EN | + FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK, + GEN_DEV_SET_OP_MODE_USB)); + nb7vpq904m_set_channel(nb7, NB7_CHNA, false); + nb7vpq904m_set_channel(nb7, NB7_CHNB, false); + } else { + regmap_write(nb7->regmap, GEN_DEV_SET_REG, + GEN_DEV_SET_CHIP_EN | + GEN_DEV_SET_CHNC_EN | + GEN_DEV_SET_CHND_EN | + FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK, + GEN_DEV_SET_OP_MODE_USB)); + nb7vpq904m_set_channel(nb7, NB7_CHNC, false); + nb7vpq904m_set_channel(nb7, NB7_CHND, false); + } + regmap_write(nb7->regmap, AUX_CC_REG, 0x2); + + return 0; + + default: + if (nb7->svid != USB_TYPEC_DP_SID) + return -EINVAL; + + break; + } + + /* DP Altmode Setup */ + + regmap_write(nb7->regmap, AUX_CC_REG, reverse ? 0x1 : 0x0); + + switch (nb7->mode) { + case TYPEC_DP_STATE_C: + case TYPEC_DP_STATE_E: + /* + * Normal Orientation (CC1) + * A -> DP3 + * B -> DP2 + * C -> DP1 + * D -> DP0 + * Flipped Orientation (CC2) + * A -> DP0 + * B -> DP1 + * C -> DP2 + * D -> DP3 + */ + regmap_write(nb7->regmap, GEN_DEV_SET_REG, + GEN_DEV_SET_CHIP_EN | + GEN_DEV_SET_CHNA_EN | + GEN_DEV_SET_CHNB_EN | + GEN_DEV_SET_CHNC_EN | + GEN_DEV_SET_CHND_EN | + FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK, + GEN_DEV_SET_OP_MODE_DP_4LANE)); + nb7vpq904m_set_channel(nb7, NB7_CHNA, true); + nb7vpq904m_set_channel(nb7, NB7_CHNB, true); + nb7vpq904m_set_channel(nb7, NB7_CHNC, true); + nb7vpq904m_set_channel(nb7, NB7_CHND, true); + break; + + case TYPEC_DP_STATE_D: + case TYPEC_DP_STATE_F: + regmap_write(nb7->regmap, GEN_DEV_SET_REG, + GEN_DEV_SET_CHIP_EN | + GEN_DEV_SET_CHNA_EN | + GEN_DEV_SET_CHNB_EN | + GEN_DEV_SET_CHNC_EN | + GEN_DEV_SET_CHND_EN | + FIELD_PREP(GEN_DEV_SET_OP_MODE_MASK, + reverse ^ nb7->swap_data_lanes ? + GEN_DEV_SET_OP_MODE_DP_CC2 + : GEN_DEV_SET_OP_MODE_DP_CC1)); + + /* + * Normal Orientation (CC1) + * A -> USB RX + * B -> USB TX + * C -> DP1 + * D -> DP0 + * Flipped Orientation (CC2) + * A -> DP0 + * B -> DP1 + * C -> USB TX + * D -> USB RX + * + * Reversed if data lanes are swapped + */ + if (nb7->swap_data_lanes) { + nb7vpq904m_set_channel(nb7, NB7_CHNA, !reverse); + nb7vpq904m_set_channel(nb7, NB7_CHNB, !reverse); + nb7vpq904m_set_channel(nb7, NB7_CHNC, reverse); + nb7vpq904m_set_channel(nb7, NB7_CHND, reverse); + } else { + nb7vpq904m_set_channel(nb7, NB7_CHNA, reverse); + nb7vpq904m_set_channel(nb7, NB7_CHNB, reverse); + nb7vpq904m_set_channel(nb7, NB7_CHNC, !reverse); + nb7vpq904m_set_channel(nb7, NB7_CHND, !reverse); + } + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int nb7vpq904m_sw_set(struct typec_switch_dev *sw, enum typec_orientation orientation) +{ + struct nb7vpq904m *nb7 = typec_switch_get_drvdata(sw); + int ret; + + ret = typec_switch_set(nb7->typec_switch, orientation); + if (ret) + return ret; + + mutex_lock(&nb7->lock); + + if (nb7->orientation != orientation) { + nb7->orientation = orientation; + + ret = nb7vpq904m_set(nb7); + } + + mutex_unlock(&nb7->lock); + + return ret; +} + +static int nb7vpq904m_retimer_set(struct typec_retimer *retimer, struct typec_retimer_state *state) +{ + struct nb7vpq904m *nb7 = typec_retimer_get_drvdata(retimer); + int ret = 0; + + mutex_lock(&nb7->lock); + + if (nb7->mode != state->mode) { + nb7->mode = state->mode; + + if (state->alt) + nb7->svid = state->alt->svid; + else + nb7->svid = 0; // No SVID + + ret = nb7vpq904m_set(nb7); + } + + mutex_unlock(&nb7->lock); + + return ret; +} + +#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DRM_PANEL_BRIDGE) +static int nb7vpq904m_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct nb7vpq904m *nb7 = container_of(bridge, struct nb7vpq904m, bridge); + struct drm_bridge *next_bridge; + + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + return -EINVAL; + + next_bridge = devm_drm_of_get_bridge(&nb7->client->dev, nb7->client->dev.of_node, 0, 0); + if (IS_ERR(next_bridge)) { + dev_err(&nb7->client->dev, "failed to acquire drm_bridge: %pe\n", next_bridge); + return PTR_ERR(next_bridge); + } + + return drm_bridge_attach(bridge->encoder, next_bridge, bridge, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); +} + +static const struct drm_bridge_funcs nb7vpq904m_bridge_funcs = { + .attach = nb7vpq904m_bridge_attach, +}; + +static int nb7vpq904m_register_bridge(struct nb7vpq904m *nb7) +{ + nb7->bridge.funcs = &nb7vpq904m_bridge_funcs; + nb7->bridge.of_node = nb7->client->dev.of_node; + + return devm_drm_bridge_add(&nb7->client->dev, &nb7->bridge); +} +#else +static int nb7vpq904m_register_bridge(struct nb7vpq904m *nb7) +{ + return 0; +} +#endif + +static const struct regmap_config nb7_regmap = { + .max_register = 0x1f, + .reg_bits = 8, + .val_bits = 8, +}; + +enum { + NORMAL_LANE_MAPPING, + INVERT_LANE_MAPPING, +}; + +#define DATA_LANES_COUNT 4 + +static const int supported_data_lane_mapping[][DATA_LANES_COUNT] = { + [NORMAL_LANE_MAPPING] = { 0, 1, 2, 3 }, + [INVERT_LANE_MAPPING] = { 3, 2, 1, 0 }, +}; + +static int nb7vpq904m_parse_data_lanes_mapping(struct nb7vpq904m *nb7) +{ + struct device_node *ep; + u32 data_lanes[4]; + int ret, i, j; + + 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; + } + + ret = of_property_read_u32_array(ep, "data-lanes", data_lanes, DATA_LANES_COUNT); + if (ret) + 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; + } + + if (j == DATA_LANES_COUNT) + 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"); + break; + default: + dev_err(&nb7->client->dev, "invalid data lanes mapping\n"); + ret = -EINVAL; + goto out_error; + } + } + +out_done: + ret = 0; + +out_error: + of_node_put(ep); + + return ret; +} + +static int nb7vpq904m_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct typec_switch_desc sw_desc = { }; + struct typec_retimer_desc retimer_desc = { }; + struct nb7vpq904m *nb7; + int ret; + + nb7 = devm_kzalloc(dev, sizeof(*nb7), GFP_KERNEL); + if (!nb7) + return -ENOMEM; + + nb7->client = client; + + nb7->regmap = devm_regmap_init_i2c(client, &nb7_regmap); + if (IS_ERR(nb7->regmap)) { + dev_err(&client->dev, "Failed to allocate register map\n"); + return PTR_ERR(nb7->regmap); + } + + nb7->mode = TYPEC_STATE_SAFE; + nb7->orientation = TYPEC_ORIENTATION_NONE; + + mutex_init(&nb7->lock); + + nb7->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(nb7->enable_gpio)) + return dev_err_probe(dev, PTR_ERR(nb7->enable_gpio), + "unable to acquire enable gpio\n"); + + nb7->vcc_supply = devm_regulator_get_optional(dev, "vcc"); + if (IS_ERR(nb7->vcc_supply)) + return PTR_ERR(nb7->vcc_supply); + + nb7->typec_switch = fwnode_typec_switch_get(dev->fwnode); + if (IS_ERR(nb7->typec_switch)) + return dev_err_probe(dev, PTR_ERR(nb7->typec_switch), + "failed to acquire orientation-switch\n"); + + ret = nb7vpq904m_parse_data_lanes_mapping(nb7); + if (ret) + return ret; + + ret = regulator_enable(nb7->vcc_supply); + if (ret) + dev_warn(dev, "Failed to enable vcc: %d\n", ret); + + gpiod_set_value(nb7->enable_gpio, 1); + + ret = nb7vpq904m_register_bridge(nb7); + if (ret) + return ret; + + sw_desc.drvdata = nb7; + sw_desc.fwnode = dev->fwnode; + sw_desc.set = nb7vpq904m_sw_set; + + nb7->sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(nb7->sw)) + return dev_err_probe(dev, PTR_ERR(nb7->sw), + "Error registering typec switch\n"); + + retimer_desc.drvdata = nb7; + retimer_desc.fwnode = dev->fwnode; + retimer_desc.set = nb7vpq904m_retimer_set; + + nb7->retimer = typec_retimer_register(dev, &retimer_desc); + if (IS_ERR(nb7->retimer)) { + typec_switch_unregister(nb7->sw); + return dev_err_probe(dev, PTR_ERR(nb7->retimer), + "Error registering typec retimer\n"); + } + + return 0; +} + +static void nb7vpq904m_remove(struct i2c_client *client) +{ + struct nb7vpq904m *nb7 = i2c_get_clientdata(client); + + typec_retimer_unregister(nb7->retimer); + typec_switch_unregister(nb7->sw); + + gpiod_set_value(nb7->enable_gpio, 0); + + regulator_disable(nb7->vcc_supply); +} + +static const struct i2c_device_id nb7vpq904m_table[] = { + { "nb7vpq904m" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nb7vpq904m_table); + +static const struct of_device_id nb7vpq904m_of_table[] = { + { .compatible = "onnn,nb7vpq904m" }, + { } +}; +MODULE_DEVICE_TABLE(of, nb7vpq904m_of_table); + +static struct i2c_driver nb7vpq904m_driver = { + .driver = { + .name = "nb7vpq904m", + .of_match_table = nb7vpq904m_of_table, + }, + .probe_new = nb7vpq904m_probe, + .remove = nb7vpq904m_remove, + .id_table = nb7vpq904m_table, +}; + +module_i2c_driver(nb7vpq904m_driver); + +MODULE_AUTHOR("Dmitry Baryshkov "); +MODULE_DESCRIPTION("OnSemi NB7VPQ904M Type-C driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 4796efdd16a8066919a3ff479e0bbc14bac816ff Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 16 Dec 2022 13:41:12 +0200 Subject: thunderbolt: Ignore data CRC mismatch for USB4 routers This is also something not always updated after the DROM contents itself so issue warning but continue parsing it as we do for pre-USB4 DROMs too. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/eeprom.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/thunderbolt/eeprom.c b/drivers/thunderbolt/eeprom.c index 0f6099c18a94..eb241b270f79 100644 --- a/drivers/thunderbolt/eeprom.c +++ b/drivers/thunderbolt/eeprom.c @@ -605,9 +605,8 @@ static int usb4_drom_parse(struct tb_switch *sw) crc = tb_crc32(sw->drom + TB_DROM_DATA_START, header->data_len); if (crc != header->data_crc32) { tb_sw_warn(sw, - "DROM data CRC32 mismatch (expected: %#x, got: %#x), aborting\n", + "DROM data CRC32 mismatch (expected: %#x, got: %#x), continuing\n", header->data_crc32, crc); - return -EINVAL; } return tb_drom_parse_entries(sw, USB4_DROM_HEADER_SIZE); -- cgit v1.2.3 From 2ad3e1314cafad9a8edbefed2b19d2a101cdb4fc Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 16 Dec 2022 16:28:05 +0200 Subject: thunderbolt: Do not touch lane 1 adapter path config space It is not required to be implemented at all because USB4 does not use lane 1 for tunneling except when aggregated with lane 0. For this reason do not try to read the path config space of USB4 lane 1 adapters. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/switch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 0c11caec7e8e..47961afdcc73 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -723,7 +723,7 @@ static int tb_init_port(struct tb_port *port) * can be read from the path config space. Legacy * devices we use hard-coded value. */ - if (tb_switch_is_usb4(port->sw)) { + if (port->cap_usb4) { struct tb_regs_hop hop; if (!tb_port_read(port, &hop, TB_CFG_HOPS, 0, 2)) -- cgit v1.2.3 From 6e21007d0f7e6723bb67e79aa3d0081419c403e8 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Fri, 23 Sep 2022 01:30:43 +0300 Subject: thunderbolt: Identify USB4 v2 routers Add a new function usb4_switch_version() that can be used to figure out the spec version of the router and make tb_switch_is_usb4() to use it as well. Update the uevent accordingly. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/switch.c | 5 +++-- drivers/thunderbolt/tb.h | 34 +++++++++++++++++++++++----------- drivers/thunderbolt/tb_regs.h | 4 ++-- 3 files changed, 28 insertions(+), 15 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 47961afdcc73..3a1fc3e053f6 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -2056,8 +2056,9 @@ static int tb_switch_uevent(const struct device *dev, struct kobj_uevent_env *en const struct tb_switch *sw = tb_to_switch(dev); const char *type; - if (sw->config.thunderbolt_version == USB4_VERSION_1_0) { - if (add_uevent_var(env, "USB4_VERSION=1.0")) + if (tb_switch_is_usb4(sw)) { + if (add_uevent_var(env, "USB4_VERSION=%u.0", + usb4_switch_version(sw))) return -ENOMEM; } diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 58df106aaa5e..bc91fcf5f430 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -947,17 +947,6 @@ static inline bool tb_switch_is_tiger_lake(const struct tb_switch *sw) return false; } -/** - * tb_switch_is_usb4() - Is the switch USB4 compliant - * @sw: Switch to check - * - * Returns true if the @sw is USB4 compliant router, false otherwise. - */ -static inline bool tb_switch_is_usb4(const struct tb_switch *sw) -{ - return sw->config.thunderbolt_version == USB4_VERSION_1_0; -} - /** * tb_switch_is_icm() - Is the switch handled by ICM firmware * @sw: Switch to check @@ -1198,6 +1187,29 @@ static inline struct tb_retimer *tb_to_retimer(struct device *dev) return NULL; } +/** + * usb4_switch_version() - Returns USB4 version of the router + * @sw: Router to check + * + * Returns major version of USB4 router (%1 for v1, %2 for v2 and so + * on). Can be called to pre-USB4 router too and in that case returns %0. + */ +static inline unsigned int usb4_switch_version(const struct tb_switch *sw) +{ + return FIELD_GET(USB4_VERSION_MAJOR_MASK, sw->config.thunderbolt_version); +} + +/** + * tb_switch_is_usb4() - Is the switch USB4 compliant + * @sw: Switch to check + * + * Returns true if the @sw is USB4 compliant router, false otherwise. + */ +static inline bool tb_switch_is_usb4(const struct tb_switch *sw) +{ + return usb4_switch_version(sw) > 0; +} + int usb4_switch_setup(struct tb_switch *sw); int usb4_switch_read_uid(struct tb_switch *sw, u64 *uid); int usb4_switch_drom_read(struct tb_switch *sw, unsigned int address, void *buf, diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index 2636423748cd..0716d6b7701a 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -190,8 +190,8 @@ struct tb_regs_switch_header { u32 thunderbolt_version:8; } __packed; -/* USB4 version 1.0 */ -#define USB4_VERSION_1_0 0x20 +/* Used with the router thunderbolt_version */ +#define USB4_VERSION_MAJOR_MASK GENMASK(7, 5) #define ROUTER_CS_1 0x01 #define ROUTER_CS_4 0x04 -- cgit v1.2.3 From e111fb92513771cbcae70d0aa855c3ca20f48c1b Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Thu, 29 Sep 2022 13:00:09 +0300 Subject: thunderbolt: Add support for USB4 v2 80 Gb/s link USB4 v2 bumps the per-lane speed up to 40 Gb/s. Also the lanes are always bonded which gives 80 Gb/s symmetric link (and 120/40 Gb/s asymmetric). This updates the speed and width of routers and XDomain connections to support the Gen 4 link. For now we keep the link as is even if it is already asymmetric. While there make tb_port_set_link_width() static. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/dma_test.c | 10 ++- drivers/thunderbolt/icm.c | 6 +- drivers/thunderbolt/switch.c | 185 ++++++++++++++++++++++++++++------------- drivers/thunderbolt/tb.c | 38 +++++++-- drivers/thunderbolt/tb.h | 14 ++-- drivers/thunderbolt/tb_regs.h | 1 + drivers/thunderbolt/xdomain.c | 82 ++++++++++++++---- include/linux/thunderbolt.h | 18 +++- 8 files changed, 266 insertions(+), 88 deletions(-) diff --git a/drivers/thunderbolt/dma_test.c b/drivers/thunderbolt/dma_test.c index 58496f889d03..39476fc48801 100644 --- a/drivers/thunderbolt/dma_test.c +++ b/drivers/thunderbolt/dma_test.c @@ -412,6 +412,7 @@ static void speed_get(const struct dma_test *dt, u64 *val) static int speed_validate(u64 val) { switch (val) { + case 40: case 20: case 10: case 0: @@ -489,9 +490,12 @@ static void dma_test_check_errors(struct dma_test *dt, int ret) if (!dt->error_code) { if (dt->link_speed && dt->xd->link_speed != dt->link_speed) { dt->error_code = DMA_TEST_SPEED_ERROR; - } else if (dt->link_width && - dt->xd->link_width != dt->link_width) { - dt->error_code = DMA_TEST_WIDTH_ERROR; + } else if (dt->link_width) { + const struct tb_xdomain *xd = dt->xd; + + if ((dt->link_width == 1 && xd->link_width != TB_LINK_WIDTH_SINGLE) || + (dt->link_width == 2 && xd->link_width < TB_LINK_WIDTH_DUAL)) + dt->error_code = DMA_TEST_WIDTH_ERROR; } else if (dt->packets_to_send != dt->packets_sent || dt->packets_to_receive != dt->packets_received || dt->crc_errors || dt->buffer_overflow_errors) { diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index 05274caf1466..dbdcad8d73bf 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -850,7 +850,8 @@ icm_fr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr) sw->security_level = security_level; sw->boot = boot; sw->link_speed = speed_gen3 ? 20 : 10; - sw->link_width = dual_lane ? 2 : 1; + sw->link_width = dual_lane ? TB_LINK_WIDTH_DUAL : + TB_LINK_WIDTH_SINGLE; sw->rpm = intel_vss_is_rtd3(pkg->ep_name, sizeof(pkg->ep_name)); if (add_switch(parent_sw, sw)) @@ -1272,7 +1273,8 @@ __icm_tr_device_connected(struct tb *tb, const struct icm_pkg_header *hdr, sw->security_level = security_level; sw->boot = boot; sw->link_speed = speed_gen3 ? 20 : 10; - sw->link_width = dual_lane ? 2 : 1; + sw->link_width = dual_lane ? TB_LINK_WIDTH_DUAL : + TB_LINK_WIDTH_SINGLE; sw->rpm = force_rtd3; if (!sw->rpm) sw->rpm = intel_vss_is_rtd3(pkg->ep_name, diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 3a1fc3e053f6..a0451218af2a 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -903,15 +903,23 @@ int tb_port_get_link_speed(struct tb_port *port) speed = (val & LANE_ADP_CS_1_CURRENT_SPEED_MASK) >> LANE_ADP_CS_1_CURRENT_SPEED_SHIFT; - return speed == LANE_ADP_CS_1_CURRENT_SPEED_GEN3 ? 20 : 10; + + switch (speed) { + case LANE_ADP_CS_1_CURRENT_SPEED_GEN4: + return 40; + case LANE_ADP_CS_1_CURRENT_SPEED_GEN3: + return 20; + default: + return 10; + } } /** * tb_port_get_link_width() - Get current link width * @port: Port to check (USB4 or CIO) * - * Returns link width. Return values can be 1 (Single-Lane), 2 (Dual-Lane) - * or negative errno in case of failure. + * Returns link width. Return the link width as encoded in &enum + * tb_link_width or negative errno in case of failure. */ int tb_port_get_link_width(struct tb_port *port) { @@ -926,11 +934,13 @@ int tb_port_get_link_width(struct tb_port *port) if (ret) return ret; + /* Matches the values in enum tb_link_width */ return (val & LANE_ADP_CS_1_CURRENT_WIDTH_MASK) >> LANE_ADP_CS_1_CURRENT_WIDTH_SHIFT; } -static bool tb_port_is_width_supported(struct tb_port *port, int width) +static bool tb_port_is_width_supported(struct tb_port *port, + unsigned int width_mask) { u32 phy, widths; int ret; @@ -946,20 +956,25 @@ static bool tb_port_is_width_supported(struct tb_port *port, int width) widths = (phy & LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK) >> LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT; - return !!(widths & width); + return widths & width_mask; +} + +static bool is_gen4_link(struct tb_port *port) +{ + return tb_port_get_link_speed(port) > 20; } /** * tb_port_set_link_width() - Set target link width of the lane adapter * @port: Lane adapter - * @width: Target link width (%1 or %2) + * @width: Target link width * * Sets the target link width of the lane adapter to @width. Does not * enable/disable lane bonding. For that call tb_port_set_lane_bonding(). * * Return: %0 in case of success and negative errno in case of error */ -int tb_port_set_link_width(struct tb_port *port, unsigned int width) +int tb_port_set_link_width(struct tb_port *port, enum tb_link_width width) { u32 val; int ret; @@ -974,11 +989,14 @@ int tb_port_set_link_width(struct tb_port *port, unsigned int width) val &= ~LANE_ADP_CS_1_TARGET_WIDTH_MASK; switch (width) { - case 1: + case TB_LINK_WIDTH_SINGLE: + /* Gen 4 link cannot be single */ + if (is_gen4_link(port)) + return -EOPNOTSUPP; val |= LANE_ADP_CS_1_TARGET_WIDTH_SINGLE << LANE_ADP_CS_1_TARGET_WIDTH_SHIFT; break; - case 2: + case TB_LINK_WIDTH_DUAL: val |= LANE_ADP_CS_1_TARGET_WIDTH_DUAL << LANE_ADP_CS_1_TARGET_WIDTH_SHIFT; break; @@ -1000,12 +1018,9 @@ int tb_port_set_link_width(struct tb_port *port, unsigned int width) * cases one should use tb_port_lane_bonding_enable() instead to enable * lane bonding. * - * As a side effect sets @port->bonding accordingly (and does the same - * for lane 1 too). - * * Return: %0 in case of success and negative errno in case of error */ -int tb_port_set_lane_bonding(struct tb_port *port, bool bonding) +static int tb_port_set_lane_bonding(struct tb_port *port, bool bonding) { u32 val; int ret; @@ -1023,19 +1038,8 @@ int tb_port_set_lane_bonding(struct tb_port *port, bool bonding) else val &= ~LANE_ADP_CS_1_LB; - ret = tb_port_write(port, &val, TB_CFG_PORT, - port->cap_phy + LANE_ADP_CS_1, 1); - if (ret) - return ret; - - /* - * When lane 0 bonding is set it will affect lane 1 too so - * update both. - */ - port->bonded = bonding; - port->dual_link_port->bonded = bonding; - - return 0; + return tb_port_write(port, &val, TB_CFG_PORT, + port->cap_phy + LANE_ADP_CS_1, 1); } /** @@ -1052,36 +1056,52 @@ int tb_port_set_lane_bonding(struct tb_port *port, bool bonding) */ int tb_port_lane_bonding_enable(struct tb_port *port) { + enum tb_link_width width; int ret; /* * Enable lane bonding for both links if not already enabled by * for example the boot firmware. */ - ret = tb_port_get_link_width(port); - if (ret == 1) { - ret = tb_port_set_link_width(port, 2); + width = tb_port_get_link_width(port); + if (width == TB_LINK_WIDTH_SINGLE) { + ret = tb_port_set_link_width(port, TB_LINK_WIDTH_DUAL); if (ret) goto err_lane0; } - ret = tb_port_get_link_width(port->dual_link_port); - if (ret == 1) { - ret = tb_port_set_link_width(port->dual_link_port, 2); + width = tb_port_get_link_width(port->dual_link_port); + if (width == TB_LINK_WIDTH_SINGLE) { + ret = tb_port_set_link_width(port->dual_link_port, + TB_LINK_WIDTH_DUAL); if (ret) goto err_lane0; } - ret = tb_port_set_lane_bonding(port, true); - if (ret) - goto err_lane1; + /* + * Only set bonding if the link was not already bonded. This + * avoids the lane adapter to re-enter bonding state. + */ + if (width == TB_LINK_WIDTH_SINGLE) { + ret = tb_port_set_lane_bonding(port, true); + if (ret) + goto err_lane1; + } + + /* + * When lane 0 bonding is set it will affect lane 1 too so + * update both. + */ + port->bonded = true; + port->dual_link_port->bonded = true; return 0; err_lane1: - tb_port_set_link_width(port->dual_link_port, 1); + tb_port_set_link_width(port->dual_link_port, TB_LINK_WIDTH_SINGLE); err_lane0: - tb_port_set_link_width(port, 1); + tb_port_set_link_width(port, TB_LINK_WIDTH_SINGLE); + return ret; } @@ -1095,27 +1115,34 @@ err_lane0: void tb_port_lane_bonding_disable(struct tb_port *port) { tb_port_set_lane_bonding(port, false); - tb_port_set_link_width(port->dual_link_port, 1); - tb_port_set_link_width(port, 1); + tb_port_set_link_width(port->dual_link_port, TB_LINK_WIDTH_SINGLE); + tb_port_set_link_width(port, TB_LINK_WIDTH_SINGLE); + port->dual_link_port->bonded = false; + port->bonded = false; } /** * tb_port_wait_for_link_width() - Wait until link reaches specific width * @port: Port to wait for - * @width: Expected link width (%1 or %2) + * @width_mask: Expected link width mask * @timeout_msec: Timeout in ms how long to wait * * Should be used after both ends of the link have been bonded (or * bonding has been disabled) to wait until the link actually reaches - * the expected state. Returns %-ETIMEDOUT if the @width was not reached - * within the given timeout, %0 if it did. + * the expected state. Returns %-ETIMEDOUT if the width was not reached + * within the given timeout, %0 if it did. Can be passed a mask of + * expected widths and succeeds if any of the widths is reached. */ -int tb_port_wait_for_link_width(struct tb_port *port, int width, +int tb_port_wait_for_link_width(struct tb_port *port, unsigned int width_mask, int timeout_msec) { ktime_t timeout = ktime_add_ms(ktime_get(), timeout_msec); int ret; + /* Gen 4 link does not support single lane */ + if ((width_mask & TB_LINK_WIDTH_SINGLE) && is_gen4_link(port)) + return -EOPNOTSUPP; + do { ret = tb_port_get_link_width(port); if (ret < 0) { @@ -1126,7 +1153,7 @@ int tb_port_wait_for_link_width(struct tb_port *port, int width, */ if (ret != -EACCES) return ret; - } else if (ret == width) { + } else if (ret & width_mask) { return 0; } @@ -1778,20 +1805,57 @@ static ssize_t speed_show(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(rx_speed, 0444, speed_show, NULL); static DEVICE_ATTR(tx_speed, 0444, speed_show, NULL); -static ssize_t lanes_show(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t rx_lanes_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct tb_switch *sw = tb_to_switch(dev); + unsigned int width; + + switch (sw->link_width) { + case TB_LINK_WIDTH_SINGLE: + case TB_LINK_WIDTH_ASYM_TX: + width = 1; + break; + case TB_LINK_WIDTH_DUAL: + width = 2; + break; + case TB_LINK_WIDTH_ASYM_RX: + width = 3; + break; + default: + WARN_ON_ONCE(1); + return -EINVAL; + } - return sysfs_emit(buf, "%u\n", sw->link_width); + return sysfs_emit(buf, "%u\n", width); } +static DEVICE_ATTR(rx_lanes, 0444, rx_lanes_show, NULL); -/* - * Currently link has same amount of lanes both directions (1 or 2) but - * expose them separately to allow possible asymmetric links in the future. - */ -static DEVICE_ATTR(rx_lanes, 0444, lanes_show, NULL); -static DEVICE_ATTR(tx_lanes, 0444, lanes_show, NULL); +static ssize_t tx_lanes_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_switch *sw = tb_to_switch(dev); + unsigned int width; + + switch (sw->link_width) { + case TB_LINK_WIDTH_SINGLE: + case TB_LINK_WIDTH_ASYM_RX: + width = 1; + break; + case TB_LINK_WIDTH_DUAL: + width = 2; + break; + case TB_LINK_WIDTH_ASYM_TX: + width = 3; + break; + default: + WARN_ON_ONCE(1); + return -EINVAL; + } + + return sysfs_emit(buf, "%u\n", width); +} +static DEVICE_ATTR(tx_lanes, 0444, tx_lanes_show, NULL); static ssize_t nvm_authenticate_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -2624,6 +2688,7 @@ int tb_switch_lane_bonding_enable(struct tb_switch *sw) { struct tb_port *up, *down; u64 route = tb_route(sw); + unsigned int width_mask; int ret; if (!route) @@ -2635,8 +2700,8 @@ int tb_switch_lane_bonding_enable(struct tb_switch *sw) up = tb_upstream_port(sw); down = tb_switch_downstream_port(sw); - if (!tb_port_is_width_supported(up, 2) || - !tb_port_is_width_supported(down, 2)) + if (!tb_port_is_width_supported(up, TB_LINK_WIDTH_DUAL) || + !tb_port_is_width_supported(down, TB_LINK_WIDTH_DUAL)) return 0; ret = tb_port_lane_bonding_enable(up); @@ -2652,7 +2717,11 @@ int tb_switch_lane_bonding_enable(struct tb_switch *sw) return ret; } - ret = tb_port_wait_for_link_width(down, 2, 100); + /* Any of the widths are all bonded */ + width_mask = TB_LINK_WIDTH_DUAL | TB_LINK_WIDTH_ASYM_TX | + TB_LINK_WIDTH_ASYM_RX; + + ret = tb_port_wait_for_link_width(down, width_mask, 100); if (ret) { tb_port_warn(down, "timeout enabling lane bonding\n"); return ret; @@ -2676,6 +2745,7 @@ int tb_switch_lane_bonding_enable(struct tb_switch *sw) void tb_switch_lane_bonding_disable(struct tb_switch *sw) { struct tb_port *up, *down; + int ret; if (!tb_route(sw)) return; @@ -2693,7 +2763,8 @@ void tb_switch_lane_bonding_disable(struct tb_switch *sw) * It is fine if we get other errors as the router might have * been unplugged. */ - if (tb_port_wait_for_link_width(down, 1, 100) == -ETIMEDOUT) + ret = tb_port_wait_for_link_width(down, TB_LINK_WIDTH_SINGLE, 100); + if (ret == -ETIMEDOUT) tb_sw_warn(sw, "timeout disabling lane bonding\n"); tb_port_update_credits(down); diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index aa6e11589c28..440693f561a4 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -570,7 +570,8 @@ static int tb_available_bandwidth(struct tb *tb, struct tb_port *src_port, usb3_consumed_down = 0; } - *available_up = *available_down = 40000; + /* Maximum possible bandwidth asymmetric Gen 4 link is 120 Gb/s */ + *available_up = *available_down = 120000; /* Find the minimum available bandwidth over all links */ tb_for_each_port_on_path(src_port, dst_port, port) { @@ -581,18 +582,45 @@ static int tb_available_bandwidth(struct tb *tb, struct tb_port *src_port, if (tb_is_upstream_port(port)) { link_speed = port->sw->link_speed; + /* + * sw->link_width is from upstream perspective + * so we use the opposite for downstream of the + * host router. + */ + if (port->sw->link_width == TB_LINK_WIDTH_ASYM_TX) { + up_bw = link_speed * 3 * 1000; + down_bw = link_speed * 1 * 1000; + } else if (port->sw->link_width == TB_LINK_WIDTH_ASYM_RX) { + up_bw = link_speed * 1 * 1000; + down_bw = link_speed * 3 * 1000; + } else { + up_bw = link_speed * port->sw->link_width * 1000; + down_bw = up_bw; + } } else { link_speed = tb_port_get_link_speed(port); if (link_speed < 0) return link_speed; - } - link_width = port->bonded ? 2 : 1; + link_width = tb_port_get_link_width(port); + if (link_width < 0) + return link_width; + + if (link_width == TB_LINK_WIDTH_ASYM_TX) { + up_bw = link_speed * 1 * 1000; + down_bw = link_speed * 3 * 1000; + } else if (link_width == TB_LINK_WIDTH_ASYM_RX) { + up_bw = link_speed * 3 * 1000; + down_bw = link_speed * 1 * 1000; + } else { + up_bw = link_speed * link_width * 1000; + down_bw = up_bw; + } + } - up_bw = link_speed * link_width * 1000; /* Mb/s */ /* Leave 10% guard band */ up_bw -= up_bw / 10; - down_bw = up_bw; + down_bw -= down_bw / 10; tb_port_dbg(port, "link total bandwidth %d/%d Mb/s\n", up_bw, down_bw); diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index bc91fcf5f430..845e851012e5 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -135,7 +135,7 @@ struct tb_switch_tmu { * @vendor_name: Name of the vendor (or %NULL if not known) * @device_name: Name of the device (or %NULL if not known) * @link_speed: Speed of the link in Gb/s - * @link_width: Width of the link (1 or 2) + * @link_width: Width of the upstream facing link * @link_usb4: Upstream link is USB4 * @generation: Switch Thunderbolt generation * @cap_plug_events: Offset to the plug events capability (%0 if not found) @@ -173,6 +173,11 @@ struct tb_switch_tmu { * switches) you need to have domain lock held. * * In USB4 terminology this structure represents a router. + * + * Note @link_width is not the same as whether link is bonded or not. + * For Gen 4 links the link is also bonded when it is asymmetric. The + * correct way to find out whether the link is bonded or not is to look + * @bonded field of the upstream port. */ struct tb_switch { struct device dev; @@ -188,7 +193,7 @@ struct tb_switch { const char *vendor_name; const char *device_name; unsigned int link_speed; - unsigned int link_width; + enum tb_link_width link_width; bool link_usb4; unsigned int generation; int cap_plug_events; @@ -1050,11 +1055,10 @@ static inline bool tb_port_use_credit_allocation(const struct tb_port *port) int tb_port_get_link_speed(struct tb_port *port); int tb_port_get_link_width(struct tb_port *port); -int tb_port_set_link_width(struct tb_port *port, unsigned int width); -int tb_port_set_lane_bonding(struct tb_port *port, bool bonding); +int tb_port_set_link_width(struct tb_port *port, enum tb_link_width width); int tb_port_lane_bonding_enable(struct tb_port *port); void tb_port_lane_bonding_disable(struct tb_port *port); -int tb_port_wait_for_link_width(struct tb_port *port, int width, +int tb_port_wait_for_link_width(struct tb_port *port, unsigned int width_mask, int timeout_msec); int tb_port_update_credits(struct tb_port *port); diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index 0716d6b7701a..69455eaf6351 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -346,6 +346,7 @@ struct tb_regs_port_header { #define LANE_ADP_CS_1_CURRENT_SPEED_SHIFT 16 #define LANE_ADP_CS_1_CURRENT_SPEED_GEN2 0x8 #define LANE_ADP_CS_1_CURRENT_SPEED_GEN3 0x4 +#define LANE_ADP_CS_1_CURRENT_SPEED_GEN4 0x2 #define LANE_ADP_CS_1_CURRENT_WIDTH_MASK GENMASK(25, 20) #define LANE_ADP_CS_1_CURRENT_WIDTH_SHIFT 20 #define LANE_ADP_CS_1_PMS BIT(30) diff --git a/drivers/thunderbolt/xdomain.c b/drivers/thunderbolt/xdomain.c index 8389961b2d45..5b5566862318 100644 --- a/drivers/thunderbolt/xdomain.c +++ b/drivers/thunderbolt/xdomain.c @@ -1290,13 +1290,16 @@ static int tb_xdomain_link_state_change(struct tb_xdomain *xd, static int tb_xdomain_bond_lanes_uuid_high(struct tb_xdomain *xd) { + unsigned int width, width_mask; struct tb_port *port; - int ret, width; + int ret; if (xd->target_link_width == LANE_ADP_CS_1_TARGET_WIDTH_SINGLE) { - width = 1; + width = TB_LINK_WIDTH_SINGLE; + width_mask = width; } else if (xd->target_link_width == LANE_ADP_CS_1_TARGET_WIDTH_DUAL) { - width = 2; + width = TB_LINK_WIDTH_DUAL; + width_mask = width | TB_LINK_WIDTH_ASYM_TX | TB_LINK_WIDTH_ASYM_RX; } else { if (xd->state_retries-- > 0) { dev_dbg(&xd->dev, @@ -1328,15 +1331,16 @@ static int tb_xdomain_bond_lanes_uuid_high(struct tb_xdomain *xd) return ret; } - ret = tb_port_wait_for_link_width(port, width, XDOMAIN_BONDING_TIMEOUT); + ret = tb_port_wait_for_link_width(port, width_mask, + XDOMAIN_BONDING_TIMEOUT); if (ret) { dev_warn(&xd->dev, "error waiting for link width to become %d\n", - width); + width_mask); return ret; } - port->bonded = width == 2; - port->dual_link_port->bonded = width == 2; + port->bonded = width > TB_LINK_WIDTH_SINGLE; + port->dual_link_port->bonded = width > TB_LINK_WIDTH_SINGLE; tb_port_update_credits(port); tb_xdomain_update_link_attributes(xd); @@ -1735,16 +1739,57 @@ static ssize_t speed_show(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(rx_speed, 0444, speed_show, NULL); static DEVICE_ATTR(tx_speed, 0444, speed_show, NULL); -static ssize_t lanes_show(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t rx_lanes_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + unsigned int width; - return sysfs_emit(buf, "%u\n", xd->link_width); + switch (xd->link_width) { + case TB_LINK_WIDTH_SINGLE: + case TB_LINK_WIDTH_ASYM_RX: + width = 1; + break; + case TB_LINK_WIDTH_DUAL: + width = 2; + break; + case TB_LINK_WIDTH_ASYM_TX: + width = 3; + break; + default: + WARN_ON_ONCE(1); + return -EINVAL; + } + + return sysfs_emit(buf, "%u\n", width); } +static DEVICE_ATTR(rx_lanes, 0444, rx_lanes_show, NULL); + +static ssize_t tx_lanes_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tb_xdomain *xd = container_of(dev, struct tb_xdomain, dev); + unsigned int width; -static DEVICE_ATTR(rx_lanes, 0444, lanes_show, NULL); -static DEVICE_ATTR(tx_lanes, 0444, lanes_show, NULL); + switch (xd->link_width) { + case TB_LINK_WIDTH_SINGLE: + case TB_LINK_WIDTH_ASYM_TX: + width = 1; + break; + case TB_LINK_WIDTH_DUAL: + width = 2; + break; + case TB_LINK_WIDTH_ASYM_RX: + width = 3; + break; + default: + WARN_ON_ONCE(1); + return -EINVAL; + } + + return sysfs_emit(buf, "%u\n", width); +} +static DEVICE_ATTR(tx_lanes, 0444, tx_lanes_show, NULL); static struct attribute *xdomain_attrs[] = { &dev_attr_device.attr, @@ -1974,6 +2019,7 @@ void tb_xdomain_remove(struct tb_xdomain *xd) */ int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd) { + unsigned int width_mask; struct tb_port *port; int ret; @@ -1997,7 +2043,12 @@ int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd) return ret; } - ret = tb_port_wait_for_link_width(port, 2, XDOMAIN_BONDING_TIMEOUT); + /* Any of the widths are all bonded */ + width_mask = TB_LINK_WIDTH_DUAL | TB_LINK_WIDTH_ASYM_TX | + TB_LINK_WIDTH_ASYM_RX; + + ret = tb_port_wait_for_link_width(port, width_mask, + XDOMAIN_BONDING_TIMEOUT); if (ret) { tb_port_warn(port, "failed to enable lane bonding\n"); return ret; @@ -2024,8 +2075,11 @@ void tb_xdomain_lane_bonding_disable(struct tb_xdomain *xd) port = tb_xdomain_downstream_port(xd); if (port->dual_link_port) { + int ret; + tb_port_lane_bonding_disable(port); - if (tb_port_wait_for_link_width(port, 1, 100) == -ETIMEDOUT) + ret = tb_port_wait_for_link_width(port, TB_LINK_WIDTH_SINGLE, 100); + if (ret == -ETIMEDOUT) tb_port_warn(port, "timeout disabling lane bonding\n"); tb_port_disable(port->dual_link_port); tb_port_update_credits(port); diff --git a/include/linux/thunderbolt.h b/include/linux/thunderbolt.h index 90cd08ab2f5d..02333f47c994 100644 --- a/include/linux/thunderbolt.h +++ b/include/linux/thunderbolt.h @@ -171,6 +171,20 @@ struct tb_property *tb_property_get_next(struct tb_property_dir *dir, int tb_register_property_dir(const char *key, struct tb_property_dir *dir); void tb_unregister_property_dir(const char *key, struct tb_property_dir *dir); +/** + * enum tb_link_width - Thunderbolt/USB4 link width + * @TB_LINK_WIDTH_SINGLE: Single lane link + * @TB_LINK_WIDTH_DUAL: Dual lane symmetric link + * @TB_LINK_WIDTH_ASYM_TX: Dual lane asymmetric Gen 4 link with 3 trasmitters + * @TB_LINK_WIDTH_ASYM_RX: Dual lane asymmetric Gen 4 link with 3 receivers + */ +enum tb_link_width { + TB_LINK_WIDTH_SINGLE = BIT(0), + TB_LINK_WIDTH_DUAL = BIT(1), + TB_LINK_WIDTH_ASYM_TX = BIT(2), + TB_LINK_WIDTH_ASYM_RX = BIT(3), +}; + /** * struct tb_xdomain - Cross-domain (XDomain) connection * @dev: XDomain device @@ -186,7 +200,7 @@ void tb_unregister_property_dir(const char *key, struct tb_property_dir *dir); * @vendor_name: Name of the vendor (or %NULL if not known) * @device_name: Name of the device (or %NULL if not known) * @link_speed: Speed of the link in Gb/s - * @link_width: Width of the link (1 or 2) + * @link_width: Width of the downstream facing link * @link_usb4: Downstream link is USB4 * @is_unplugged: The XDomain is unplugged * @needs_uuid: If the XDomain does not have @remote_uuid it will be @@ -234,7 +248,7 @@ struct tb_xdomain { const char *vendor_name; const char *device_name; unsigned int link_speed; - unsigned int link_width; + enum tb_link_width link_width; bool link_usb4; bool is_unplugged; bool needs_uuid; -- cgit v1.2.3 From 235d019481bcaa38a1d407f8513c54209aa387b8 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 23 Dec 2022 10:45:51 +0200 Subject: thunderbolt: Add the new USB4 v2 notification types USB4 v2 spec adds a bunch of new notifications that the connection manager can use instead of polling. While we do not use these yet we need to ack the ones routers expect to be acked. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/ctl.c | 28 ++++++++++++++++++++++++++++ drivers/thunderbolt/tb.c | 17 +++++++++++++---- drivers/thunderbolt/tb_msgs.h | 7 +++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index 3a213322ec7a..d997a4c545f7 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -409,6 +409,13 @@ static int tb_async_error(const struct ctl_pkg *pkg) case TB_CFG_ERROR_HEC_ERROR_DETECTED: case TB_CFG_ERROR_FLOW_CONTROL_ERROR: case TB_CFG_ERROR_DP_BW: + case TB_CFG_ERROR_ROP_CMPLT: + case TB_CFG_ERROR_POP_CMPLT: + case TB_CFG_ERROR_PCIE_WAKE: + case TB_CFG_ERROR_DP_CON_CHANGE: + case TB_CFG_ERROR_DPTX_DISCOVERY: + case TB_CFG_ERROR_LINK_RECOVERY: + case TB_CFG_ERROR_ASYM_LINK: return true; default: @@ -758,6 +765,27 @@ int tb_cfg_ack_notification(struct tb_ctl *ctl, u64 route, case TB_CFG_ERROR_DP_BW: name = "DP_BW"; break; + case TB_CFG_ERROR_ROP_CMPLT: + name = "router operation completion"; + break; + case TB_CFG_ERROR_POP_CMPLT: + name = "port operation completion"; + break; + case TB_CFG_ERROR_PCIE_WAKE: + name = "PCIe wake"; + break; + case TB_CFG_ERROR_DP_CON_CHANGE: + name = "DP connector change"; + break; + case TB_CFG_ERROR_DPTX_DISCOVERY: + name = "DPTX discovery"; + break; + case TB_CFG_ERROR_LINK_RECOVERY: + name = "link recovery"; + break; + case TB_CFG_ERROR_ASYM_LINK: + name = "asymmetric link"; + break; default: name = "unknown"; break; diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 440693f561a4..f18cb5a52f0b 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -1952,17 +1952,26 @@ static void tb_queue_dp_bandwidth_request(struct tb *tb, u64 route, u8 port) static void tb_handle_notification(struct tb *tb, u64 route, const struct cfg_error_pkg *error) { - if (tb_cfg_ack_notification(tb->ctl, route, error)) - tb_warn(tb, "could not ack notification on %llx\n", route); switch (error->error) { + case TB_CFG_ERROR_PCIE_WAKE: + case TB_CFG_ERROR_DP_CON_CHANGE: + case TB_CFG_ERROR_DPTX_DISCOVERY: + if (tb_cfg_ack_notification(tb->ctl, route, error)) + tb_warn(tb, "could not ack notification on %llx\n", + route); + break; + case TB_CFG_ERROR_DP_BW: + if (tb_cfg_ack_notification(tb->ctl, route, error)) + tb_warn(tb, "could not ack notification on %llx\n", + route); tb_queue_dp_bandwidth_request(tb, route, error->port); break; default: - /* Ack is enough */ - return; + /* Ignore for now */ + break; } } diff --git a/drivers/thunderbolt/tb_msgs.h b/drivers/thunderbolt/tb_msgs.h index 3234bff07899..cd750e4b3440 100644 --- a/drivers/thunderbolt/tb_msgs.h +++ b/drivers/thunderbolt/tb_msgs.h @@ -30,6 +30,13 @@ enum tb_cfg_error { TB_CFG_ERROR_FLOW_CONTROL_ERROR = 13, TB_CFG_ERROR_LOCK = 15, TB_CFG_ERROR_DP_BW = 32, + TB_CFG_ERROR_ROP_CMPLT = 33, + TB_CFG_ERROR_POP_CMPLT = 34, + TB_CFG_ERROR_PCIE_WAKE = 35, + TB_CFG_ERROR_DP_CON_CHANGE = 36, + TB_CFG_ERROR_DPTX_DISCOVERY = 37, + TB_CFG_ERROR_LINK_RECOVERY = 38, + TB_CFG_ERROR_ASYM_LINK = 39, }; /* common header */ -- cgit v1.2.3 From 0fc70886569c8459c4494ba9ef8c4ef34b81e781 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 23 Dec 2022 16:59:00 +0200 Subject: thunderbolt: Reset USB4 v2 host router USB4 v2 added a bit that can be used to reset the host router so we use this to trigger reset when the driver probes. This will reset the already connected topology as well but doing this simplifies things a lot if for instance the link is already set to asymmetric. We also add a module parameter to prevent this in case of problems. While there rename the REG_HOP_COUNT to REG_CAPS to match the USB4 spec naming better. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/nhi.c | 39 ++++++++++++++++++++++++++++++++++++++- drivers/thunderbolt/nhi_regs.h | 19 ++++++++++++------- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index a979f47109e3..116016695a6a 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -46,6 +46,10 @@ #define QUIRK_AUTO_CLEAR_INT BIT(0) #define QUIRK_E2E BIT(1) +static bool host_reset = true; +module_param(host_reset, bool, 0444); +MODULE_PARM_DESC(host_reset, "reset USBv2 host router (default: true)"); + static int ring_interrupt_index(const struct tb_ring *ring) { int bit = ring->hop; @@ -1217,6 +1221,37 @@ static void nhi_check_iommu(struct tb_nhi *nhi) str_enabled_disabled(port_ok)); } +static void nhi_reset(struct tb_nhi *nhi) +{ + ktime_t timeout; + u32 val; + + val = ioread32(nhi->iobase + REG_CAPS); + /* Reset only v2 and later routers */ + if (FIELD_GET(REG_CAPS_VERSION_MASK, val) < REG_CAPS_VERSION_2) + return; + + if (!host_reset) { + dev_dbg(&nhi->pdev->dev, "skipping host router reset\n"); + return; + } + + iowrite32(REG_RESET_HRR, nhi->iobase + REG_RESET); + msleep(100); + + timeout = ktime_add_ms(ktime_get(), 500); + do { + val = ioread32(nhi->iobase + REG_RESET); + if (!(val & REG_RESET_HRR)) { + dev_warn(&nhi->pdev->dev, "host router reset successful\n"); + return; + } + usleep_range(10, 20); + } while (ktime_before(ktime_get(), timeout)); + + dev_warn(&nhi->pdev->dev, "timeout resetting host router\n"); +} + static int nhi_init_msi(struct tb_nhi *nhi) { struct pci_dev *pdev = nhi->pdev; @@ -1317,7 +1352,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) nhi->ops = (const struct tb_nhi_ops *)id->driver_data; /* cannot fail - table is allocated in pcim_iomap_regions */ nhi->iobase = pcim_iomap_table(pdev)[0]; - nhi->hop_count = ioread32(nhi->iobase + REG_HOP_COUNT) & 0x3ff; + nhi->hop_count = ioread32(nhi->iobase + REG_CAPS) & 0x3ff; dev_dbg(dev, "total paths: %d\n", nhi->hop_count); nhi->tx_rings = devm_kcalloc(&pdev->dev, nhi->hop_count, @@ -1330,6 +1365,8 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id) nhi_check_quirks(nhi); nhi_check_iommu(nhi); + nhi_reset(nhi); + res = nhi_init_msi(nhi); if (res) return dev_err_probe(dev, res, "cannot enable MSI, aborting\n"); diff --git a/drivers/thunderbolt/nhi_regs.h b/drivers/thunderbolt/nhi_regs.h index 6ba295815477..297a3e440648 100644 --- a/drivers/thunderbolt/nhi_regs.h +++ b/drivers/thunderbolt/nhi_regs.h @@ -37,7 +37,7 @@ struct ring_desc { /* NHI registers in bar 0 */ /* - * 16 bytes per entry, one entry for every hop (REG_HOP_COUNT) + * 16 bytes per entry, one entry for every hop (REG_CAPS) * 00: physical pointer to an array of struct ring_desc * 08: ring tail (set by NHI) * 10: ring head (index of first non posted descriptor) @@ -46,7 +46,7 @@ struct ring_desc { #define REG_TX_RING_BASE 0x00000 /* - * 16 bytes per entry, one entry for every hop (REG_HOP_COUNT) + * 16 bytes per entry, one entry for every hop (REG_CAPS) * 00: physical pointer to an array of struct ring_desc * 08: ring head (index of first not posted descriptor) * 10: ring tail (set by NHI) @@ -56,7 +56,7 @@ struct ring_desc { #define REG_RX_RING_BASE 0x08000 /* - * 32 bytes per entry, one entry for every hop (REG_HOP_COUNT) + * 32 bytes per entry, one entry for every hop (REG_CAPS) * 00: enum_ring_flags * 04: isoch time stamp ?? (write 0) * ..: unknown @@ -64,7 +64,7 @@ struct ring_desc { #define REG_TX_OPTIONS_BASE 0x19800 /* - * 32 bytes per entry, one entry for every hop (REG_HOP_COUNT) + * 32 bytes per entry, one entry for every hop (REG_CAPS) * 00: enum ring_flags * If RING_FLAG_E2E_FLOW_CONTROL is set then bits 13-23 must be set to * the corresponding TX hop id. @@ -77,7 +77,7 @@ struct ring_desc { /* * three bitfields: tx, rx, rx overflow - * Every bitfield contains one bit for every hop (REG_HOP_COUNT). + * Every bitfield contains one bit for every hop (REG_CAPS). * New interrupts are fired only after ALL registers have been * read (even those containing only disabled rings). */ @@ -87,7 +87,7 @@ struct ring_desc { /* * two bitfields: rx, tx - * Both bitfields contains one bit for every hop (REG_HOP_COUNT). To + * Both bitfields contains one bit for every hop (REG_CAPS). To * enable/disable interrupts set/clear the corresponding bits. */ #define REG_RING_INTERRUPT_BASE 0x38200 @@ -104,12 +104,17 @@ struct ring_desc { #define REG_INT_VEC_ALLOC_REGS (32 / REG_INT_VEC_ALLOC_BITS) /* The last 11 bits contain the number of hops supported by the NHI port. */ -#define REG_HOP_COUNT 0x39640 +#define REG_CAPS 0x39640 +#define REG_CAPS_VERSION_MASK GENMASK(23, 16) +#define REG_CAPS_VERSION_2 0x40 #define REG_DMA_MISC 0x39864 #define REG_DMA_MISC_INT_AUTO_CLEAR BIT(2) #define REG_DMA_MISC_DISABLE_AUTO_CLEAR BIT(17) +#define REG_RESET 0x39898 +#define REG_RESET_HRR BIT(0) + #define REG_INMAIL_DATA 0x39900 #define REG_INMAIL_CMD 0x39904 -- cgit v1.2.3 From 14200a2631dd1f041201985e2a757d2d06ba2524 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Thu, 29 Sep 2022 13:17:24 +0300 Subject: thunderbolt: Announce USB4 v2 connection manager support Program the CMUV (Connection Manager USB4 Version) field for USB4 v2 and v1 routers according to the spec. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/switch.c | 8 ++++++-- drivers/thunderbolt/tb_regs.h | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index a0451218af2a..ebe9559c8c79 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -2430,9 +2430,13 @@ int tb_switch_configure(struct tb_switch *sw) /* * For USB4 devices, we need to program the CM version * accordingly so that it knows to expose all the - * additional capabilities. + * additional capabilities. Program it according to USB4 + * version to avoid changing existing (v1) routers behaviour. */ - sw->config.cmuv = USB4_VERSION_1_0; + if (usb4_switch_version(sw) < 2) + sw->config.cmuv = ROUTER_CS_4_CMUV_V1; + else + sw->config.cmuv = ROUTER_CS_4_CMUV_V2; sw->config.plug_events_delay = 0xa; /* Enumerate the switch */ diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index 69455eaf6351..c8e40ef09903 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -195,6 +195,9 @@ struct tb_regs_switch_header { #define ROUTER_CS_1 0x01 #define ROUTER_CS_4 0x04 +/* Used with the router cmuv field */ +#define ROUTER_CS_4_CMUV_V1 0x10 +#define ROUTER_CS_4_CMUV_V2 0x20 #define ROUTER_CS_5 0x05 #define ROUTER_CS_5_SLP BIT(0) #define ROUTER_CS_5_WOP BIT(1) -- cgit v1.2.3 From 6e19d48ea0d8aeee5688e5718cf2143d281864f3 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Thu, 29 Sep 2022 12:49:48 +0300 Subject: thunderbolt: Enable USB4 v2 PCIe TLP/DLLP extended encapsulation USB4 v2 spec introduces modified encapsulation of PCIe TLP and DLLP packets. This improves the PCIe tunneled traffic usage by reducing overhead. Enable this if both sides of the link support it. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.h | 2 ++ drivers/thunderbolt/tb_regs.h | 2 ++ drivers/thunderbolt/tunnel.c | 38 +++++++++++++++++++++++++++++++++++--- drivers/thunderbolt/usb4.c | 31 +++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 845e851012e5..002e0426a82c 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1301,6 +1301,8 @@ int usb4_dp_port_allocated_bw(struct tb_port *port); int usb4_dp_port_allocate_bw(struct tb_port *port, int bw); int usb4_dp_port_requested_bw(struct tb_port *port); +int usb4_pci_port_set_ext_encapsulation(struct tb_port *port, bool enable); + static inline bool tb_is_usb4_port_device(const struct device *dev) { return dev->type == &usb4_port_device_type; diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index c8e40ef09903..549cc79c7313 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -451,6 +451,8 @@ struct tb_regs_port_header { /* PCIe adapter registers */ #define ADP_PCIE_CS_0 0x00 #define ADP_PCIE_CS_0_PE BIT(31) +#define ADP_PCIE_CS_1 0x01 +#define ADP_PCIE_CS_1_EE BIT(0) /* USB adapter registers */ #define ADP_USB3_CS_0 0x00 diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 7df5f90e21d4..c8ca0a41dac8 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "tunnel.h" #include "tb.h" @@ -153,18 +154,49 @@ static struct tb_tunnel *tb_tunnel_alloc(struct tb *tb, size_t npaths, return tunnel; } +static int tb_pci_set_ext_encapsulation(struct tb_tunnel *tunnel, bool enable) +{ + int ret; + + /* Only supported of both routers are at least USB4 v2 */ + if (usb4_switch_version(tunnel->src_port->sw) < 2 || + usb4_switch_version(tunnel->dst_port->sw) < 2) + return 0; + + ret = usb4_pci_port_set_ext_encapsulation(tunnel->src_port, enable); + if (ret) + return ret; + + ret = usb4_pci_port_set_ext_encapsulation(tunnel->dst_port, enable); + if (ret) + return ret; + + tb_tunnel_dbg(tunnel, "extended encapsulation %s\n", + str_enabled_disabled(enable)); + return 0; +} + static int tb_pci_activate(struct tb_tunnel *tunnel, bool activate) { int res; + if (activate) { + res = tb_pci_set_ext_encapsulation(tunnel, activate); + if (res) + return res; + } + res = tb_pci_port_enable(tunnel->src_port, activate); if (res) return res; - if (tb_port_is_pcie_up(tunnel->dst_port)) - return tb_pci_port_enable(tunnel->dst_port, activate); + if (tb_port_is_pcie_up(tunnel->dst_port)) { + res = tb_pci_port_enable(tunnel->dst_port, activate); + if (res) + return res; + } - return 0; + return activate ? 0 : tb_pci_set_ext_encapsulation(tunnel, activate); } static int tb_pci_init_credits(struct tb_path_hop *hop) diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 9f5a98347bee..302d8d3fbd5a 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -2796,3 +2796,34 @@ int usb4_dp_port_requested_bw(struct tb_port *port) return (val & ADP_DP_CS_8_REQUESTED_BW_MASK) * granularity; } + +/** + * usb4_pci_port_set_ext_encapsulation() - Enable/disable extended encapsulation + * @port: PCIe adapter + * @enable: Enable/disable extended encapsulation + * + * Enables or disables extended encapsulation used in PCIe tunneling. Caller + * needs to make sure both adapters support this before enabling. Returns %0 on + * success and negative errno otherwise. + */ +int usb4_pci_port_set_ext_encapsulation(struct tb_port *port, bool enable) +{ + u32 val; + int ret; + + if (!tb_port_is_pcie_up(port) && !tb_port_is_pcie_down(port)) + return -EINVAL; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_PCIE_CS_1, 1); + if (ret) + return ret; + + if (enable) + val |= ADP_PCIE_CS_1_EE; + else + val &= ~ADP_PCIE_CS_1_EE; + + return tb_port_write(port, &val, TB_CFG_PORT, + port->cap_adap + ADP_PCIE_CS_1, 1); +} -- cgit v1.2.3 From ee22d52aeef1ed417fc7fddc6a7ba047e78f7003 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Wed, 25 Jan 2023 11:48:12 +0200 Subject: thunderbolt: Add two additional double words for adapters TMU for USB4 v2 routers For USB4 v2 routers, the adapters's TMU capability has two additional double words. Include them in the debugfs register dump. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/debugfs.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 40b59e662ee3..48aaba17d1db 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -19,7 +19,8 @@ #define PORT_CAP_LANE_LEN 3 #define PORT_CAP_USB3_LEN 5 #define PORT_CAP_DP_LEN 8 -#define PORT_CAP_TMU_LEN 8 +#define PORT_CAP_TMU_V1_LEN 8 +#define PORT_CAP_TMU_V2_LEN 10 #define PORT_CAP_BASIC_LEN 9 #define PORT_CAP_USB4_LEN 20 @@ -1161,7 +1162,10 @@ static void port_cap_show(struct tb_port *port, struct seq_file *s, break; case TB_PORT_CAP_TIME1: - length = PORT_CAP_TMU_LEN; + if (usb4_switch_version(port->sw) < 2) + length = PORT_CAP_TMU_V1_LEN; + else + length = PORT_CAP_TMU_V2_LEN; break; case TB_PORT_CAP_POWER: -- cgit v1.2.3 From 75abb4f5fff2314eef15887663a8dcfa062b4f67 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Wed, 25 Jan 2023 11:48:13 +0200 Subject: thunderbolt: Fix DisplayPort IN adapter capability length for USB4 v2 routers For USB4 v2 routers, the DisplayPort IN adapter capability length is longer. Display the correct capability length in the debugfs register dump. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/debugfs.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 48aaba17d1db..78bcf77831fe 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -18,7 +18,8 @@ #define PORT_CAP_POWER_LEN 2 #define PORT_CAP_LANE_LEN 3 #define PORT_CAP_USB3_LEN 5 -#define PORT_CAP_DP_LEN 8 +#define PORT_CAP_DP_V1_LEN 9 +#define PORT_CAP_DP_V2_LEN 14 #define PORT_CAP_TMU_V1_LEN 8 #define PORT_CAP_TMU_V2_LEN 10 #define PORT_CAP_BASIC_LEN 9 @@ -1175,11 +1176,13 @@ static void port_cap_show(struct tb_port *port, struct seq_file *s, case TB_PORT_CAP_ADAP: if (tb_port_is_pcie_down(port) || tb_port_is_pcie_up(port)) { length = PORT_CAP_PCIE_LEN; - } else if (tb_port_is_dpin(port) || tb_port_is_dpout(port)) { - if (usb4_dp_port_bw_mode_supported(port)) - length = PORT_CAP_DP_LEN + 1; + } else if (tb_port_is_dpin(port)) { + if (usb4_switch_version(port->sw) < 2) + length = PORT_CAP_DP_V1_LEN; else - length = PORT_CAP_DP_LEN; + length = PORT_CAP_DP_V2_LEN; + } else if (tb_port_is_dpout(port)) { + length = PORT_CAP_DP_V1_LEN; } else if (tb_port_is_usb3_down(port) || tb_port_is_usb3_up(port)) { length = PORT_CAP_USB3_LEN; -- cgit v1.2.3 From 0209c808a56e6469f0ed50dd2e70c3aec074bf90 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Wed, 25 Jan 2023 11:48:14 +0200 Subject: thunderbolt: Fix PCIe adapter capability length for USB4 v2 routers For USB4 v2 routers, the PCIe adapter capability length is longer. Display the correct capability length in the debugfs register dump. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/debugfs.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/thunderbolt/debugfs.c b/drivers/thunderbolt/debugfs.c index 78bcf77831fe..c9ddd49138d8 100644 --- a/drivers/thunderbolt/debugfs.c +++ b/drivers/thunderbolt/debugfs.c @@ -14,7 +14,8 @@ #include "tb.h" #include "sb_regs.h" -#define PORT_CAP_PCIE_LEN 1 +#define PORT_CAP_V1_PCIE_LEN 1 +#define PORT_CAP_V2_PCIE_LEN 2 #define PORT_CAP_POWER_LEN 2 #define PORT_CAP_LANE_LEN 3 #define PORT_CAP_USB3_LEN 5 @@ -1175,7 +1176,10 @@ static void port_cap_show(struct tb_port *port, struct seq_file *s, case TB_PORT_CAP_ADAP: if (tb_port_is_pcie_down(port) || tb_port_is_pcie_up(port)) { - length = PORT_CAP_PCIE_LEN; + if (usb4_switch_version(port->sw) < 2) + length = PORT_CAP_V1_PCIE_LEN; + else + length = PORT_CAP_V2_PCIE_LEN; } else if (tb_port_is_dpin(port)) { if (usb4_switch_version(port->sw) < 2) length = PORT_CAP_DP_V1_LEN; -- cgit v1.2.3 From 6f14a210661ce03988ef4ed3c8402037c8e06539 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Sat, 17 Dec 2022 08:35:04 +0200 Subject: thunderbolt: Add Intel Barlow Ridge PCI ID Intel Barlow Ridge is the first USB4 v2 controller from Intel. The controller exposes standard USB4 PCI class ID in typical configurations, however there is a way to configure it so that it uses a special class ID to allow using s different driver than the Windows inbox one. For this reason add the Barlow Ridge PCI ID to the Linux driver too so that the driver can attach regardless of the class ID. Tested-by: Pengfei Xu Signed-off-by: Mika Westerberg --- drivers/thunderbolt/nhi.c | 2 ++ drivers/thunderbolt/nhi.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 116016695a6a..4b7bec74e89f 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -1517,6 +1517,8 @@ static struct pci_device_id nhi_ids[] = { .driver_data = (kernel_ulong_t)&icl_nhi_ops }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_MTL_P_NHI1), .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_80G_NHI) }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_40G_NHI) }, /* Any USB4 compliant host */ { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_USB4, ~0) }, diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h index b0718020c6f5..c15a0c46c9cf 100644 --- a/drivers/thunderbolt/nhi.h +++ b/drivers/thunderbolt/nhi.h @@ -75,6 +75,8 @@ extern const struct tb_nhi_ops icl_nhi_ops; #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE 0x15ef #define PCI_DEVICE_ID_INTEL_ADL_NHI0 0x463e #define PCI_DEVICE_ID_INTEL_ADL_NHI1 0x466d +#define PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_80G_NHI 0x5781 +#define PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_40G_NHI 0x5784 #define PCI_DEVICE_ID_INTEL_MTL_M_NHI0 0x7eb2 #define PCI_DEVICE_ID_INTEL_MTL_P_NHI0 0x7ec2 #define PCI_DEVICE_ID_INTEL_MTL_P_NHI1 0x7ec3 -- cgit v1.2.3 From f2bfa944080dcbb8eb56259dfd2c07204cbee17e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 17 May 2023 10:45:53 +0300 Subject: thunderbolt: Limit Intel Barlow Ridge USB3 bandwidth Intel Barlow Ridge discrete USB4 host router has the same limitation as the previous generations so make sure the USB3 bandwidth limitation quirk is applied to Barlow Ridge too. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/nhi.h | 2 ++ drivers/thunderbolt/quirks.c | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h index c15a0c46c9cf..0f029ce75882 100644 --- a/drivers/thunderbolt/nhi.h +++ b/drivers/thunderbolt/nhi.h @@ -77,6 +77,8 @@ extern const struct tb_nhi_ops icl_nhi_ops; #define PCI_DEVICE_ID_INTEL_ADL_NHI1 0x466d #define PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_80G_NHI 0x5781 #define PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_40G_NHI 0x5784 +#define PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HUB_80G_BRIDGE 0x5786 +#define PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HUB_40G_BRIDGE 0x57a4 #define PCI_DEVICE_ID_INTEL_MTL_M_NHI0 0x7eb2 #define PCI_DEVICE_ID_INTEL_MTL_P_NHI0 0x7ec2 #define PCI_DEVICE_ID_INTEL_MTL_P_NHI1 0x7ec3 diff --git a/drivers/thunderbolt/quirks.c b/drivers/thunderbolt/quirks.c index 854d84148850..488138a28ae1 100644 --- a/drivers/thunderbolt/quirks.c +++ b/drivers/thunderbolt/quirks.c @@ -75,6 +75,14 @@ static const struct tb_quirk tb_quirks[] = { quirk_usb3_maximum_bandwidth }, { 0x8087, PCI_DEVICE_ID_INTEL_MTL_P_NHI1, 0x0000, 0x0000, quirk_usb3_maximum_bandwidth }, + { 0x8087, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_80G_NHI, 0x0000, 0x0000, + quirk_usb3_maximum_bandwidth }, + { 0x8087, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HOST_40G_NHI, 0x0000, 0x0000, + quirk_usb3_maximum_bandwidth }, + { 0x8087, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HUB_80G_BRIDGE, 0x0000, 0x0000, + quirk_usb3_maximum_bandwidth }, + { 0x8087, PCI_DEVICE_ID_INTEL_BARLOW_RIDGE_HUB_40G_BRIDGE, 0x0000, 0x0000, + quirk_usb3_maximum_bandwidth }, /* * CLx is not supported on AMD USB4 Yellow Carp and Pink Sardine platforms. */ -- cgit v1.2.3 From 7c81a578cbd1124265c07895395f0a5f30fab5d1 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Thu, 11 May 2023 11:19:19 +0300 Subject: thunderbolt: Move constants related to NVM into nvm.c Move constants related to NVM into nvm.c to make the code cleaner. Use a separate constant for USB4_DATA_DWORDS in usb4.c. No functional changes. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/nvm.c | 4 ++++ drivers/thunderbolt/tb.h | 4 ---- drivers/thunderbolt/usb4.c | 11 ++++++----- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/thunderbolt/nvm.c b/drivers/thunderbolt/nvm.c index 3dd5f81bd629..b004f29ad2b6 100644 --- a/drivers/thunderbolt/nvm.c +++ b/drivers/thunderbolt/nvm.c @@ -12,6 +12,10 @@ #include "tb.h" +#define NVM_MIN_SIZE SZ_32K +#define NVM_MAX_SIZE SZ_512K +#define NVM_DATA_DWORDS 16 + /* Intel specific NVM offsets */ #define INTEL_NVM_DEVID 0x05 #define INTEL_NVM_VERSION 0x08 diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 002e0426a82c..088033726648 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -19,10 +19,6 @@ #include "ctl.h" #include "dma_port.h" -#define NVM_MIN_SIZE SZ_32K -#define NVM_MAX_SIZE SZ_512K -#define NVM_DATA_DWORDS 16 - /* Keep link controller awake during update */ #define QUIRK_FORCE_POWER_LINK_CONTROLLER BIT(0) /* Disable CLx if not supported */ diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index 302d8d3fbd5a..d203b6594672 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -15,6 +15,7 @@ #include "tb.h" #define USB4_DATA_RETRIES 3 +#define USB4_DATA_DWORDS 16 enum usb4_sb_target { USB4_SB_TARGET_ROUTER, @@ -112,7 +113,7 @@ static int __usb4_switch_op(struct tb_switch *sw, u16 opcode, u32 *metadata, { const struct tb_cm_ops *cm_ops = sw->tb->cm_ops; - if (tx_dwords > NVM_DATA_DWORDS || rx_dwords > NVM_DATA_DWORDS) + if (tx_dwords > USB4_DATA_DWORDS || rx_dwords > USB4_DATA_DWORDS) return -EINVAL; /* @@ -702,7 +703,7 @@ int usb4_switch_credits_init(struct tb_switch *sw) int max_usb3, min_dp_aux, min_dp_main, max_pcie, max_dma; int ret, length, i, nports; const struct tb_port *port; - u32 data[NVM_DATA_DWORDS]; + u32 data[USB4_DATA_DWORDS]; u32 metadata = 0; u8 status = 0; @@ -1198,7 +1199,7 @@ static int usb4_port_wait_for_bit(struct tb_port *port, u32 offset, u32 bit, static int usb4_port_read_data(struct tb_port *port, void *data, size_t dwords) { - if (dwords > NVM_DATA_DWORDS) + if (dwords > USB4_DATA_DWORDS) return -EINVAL; return tb_port_read(port, data, TB_CFG_PORT, port->cap_usb4 + PORT_CS_2, @@ -1208,7 +1209,7 @@ static int usb4_port_read_data(struct tb_port *port, void *data, size_t dwords) static int usb4_port_write_data(struct tb_port *port, const void *data, size_t dwords) { - if (dwords > NVM_DATA_DWORDS) + if (dwords > USB4_DATA_DWORDS) return -EINVAL; return tb_port_write(port, data, TB_CFG_PORT, port->cap_usb4 + PORT_CS_2, @@ -1844,7 +1845,7 @@ static int usb4_port_retimer_nvm_read_block(void *data, unsigned int dwaddress, int ret; metadata = dwaddress << USB4_NVM_READ_OFFSET_SHIFT; - if (dwords < NVM_DATA_DWORDS) + if (dwords < USB4_DATA_DWORDS) metadata |= dwords << USB4_NVM_READ_LENGTH_SHIFT; ret = usb4_port_retimer_write(port, index, USB4_SB_METADATA, &metadata, -- cgit v1.2.3 From 322ff701ffed52fb1cade855b2145a3b4316ccd7 Mon Sep 17 00:00:00 2001 From: Gil Fine Date: Thu, 11 May 2023 11:19:20 +0300 Subject: thunderbolt: Increase NVM_MAX_SIZE to support Intel Barlow Ridge controller Intel Barlow Ridge discrete USB4 controller has larger NOR Flash, hence increase NVM_MAX_SIZE to support it. Signed-off-by: Gil Fine Signed-off-by: Mika Westerberg --- drivers/thunderbolt/nvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thunderbolt/nvm.c b/drivers/thunderbolt/nvm.c index b004f29ad2b6..69fb3b0fa34f 100644 --- a/drivers/thunderbolt/nvm.c +++ b/drivers/thunderbolt/nvm.c @@ -13,7 +13,7 @@ #include "tb.h" #define NVM_MIN_SIZE SZ_32K -#define NVM_MAX_SIZE SZ_512K +#define NVM_MAX_SIZE SZ_1M #define NVM_DATA_DWORDS 16 /* Intel specific NVM offsets */ -- cgit v1.2.3 From d49b4f043d63bddf4c1836623b8ae800878ed2e3 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 11 Oct 2022 12:11:09 +0300 Subject: thunderbolt: Add support for enhanced uni-directional TMU mode This is new TMU mode introduced with the USB4 v2. This mode is simpler than the existing ones and allows all CL states as well. Enable this for all links where both side routers are v2 and keep the existing functionality for the v1 and earlier links. Currently only support the MedRes rate. We can add the HiFi rate later too if it turns out to be useful. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/switch.c | 16 ++ drivers/thunderbolt/tb.c | 58 +++- drivers/thunderbolt/tb.h | 75 +++--- drivers/thunderbolt/tb_regs.h | 12 +- drivers/thunderbolt/tmu.c | 595 +++++++++++++++++++++++++++++++++--------- drivers/thunderbolt/usb4.c | 31 ++- 6 files changed, 614 insertions(+), 173 deletions(-) diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index ebe9559c8c79..7ea63bb31714 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -2466,6 +2466,22 @@ int tb_switch_configure(struct tb_switch *sw) return tb_plug_events_active(sw, true); } +/** + * tb_switch_configuration_valid() - Set the tunneling configuration to be valid + * @sw: Router to configure + * + * Needs to be called before any tunnels can be setup through the + * router. Can be called to any router. + * + * Returns %0 in success and negative errno otherwise. + */ +int tb_switch_configuration_valid(struct tb_switch *sw) +{ + if (tb_switch_is_usb4(sw)) + return usb4_switch_configuration_valid(sw); + return 0; +} + static int tb_switch_set_uuid(struct tb_switch *sw) { bool uid = false; diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index f18cb5a52f0b..ff034975a87e 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -297,11 +297,23 @@ static int tb_increase_switch_tmu_accuracy(struct device *dev, void *data) struct tb_switch *sw; sw = tb_to_switch(dev); - if (sw) { - tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, - tb_switch_clx_is_enabled(sw, TB_CL1)); - if (tb_switch_tmu_enable(sw)) - tb_sw_warn(sw, "failed to increase TMU rate\n"); + if (!sw) + return 0; + + if (tb_switch_tmu_is_configured(sw, TB_SWITCH_TMU_MODE_LOWRES)) { + enum tb_switch_tmu_mode mode; + int ret; + + if (tb_switch_clx_is_enabled(sw, TB_CL1)) + mode = TB_SWITCH_TMU_MODE_HIFI_UNI; + else + mode = TB_SWITCH_TMU_MODE_HIFI_BI; + + ret = tb_switch_tmu_configure(sw, mode); + if (ret) + return ret; + + return tb_switch_tmu_enable(sw); } return 0; @@ -319,6 +331,9 @@ static void tb_increase_tmu_accuracy(struct tb_tunnel *tunnel) * accuracy of first depth child routers (and the host router) * to the highest. This is needed for the DP tunneling to work * but also allows CL0s. + * + * If both routers are v2 then we don't need to do anything as + * they are using enhanced TMU mode that allows all CLx. */ sw = tunnel->tb->root_switch; device_for_each_child(&sw->dev, NULL, tb_increase_switch_tmu_accuracy); @@ -329,14 +344,22 @@ static int tb_enable_tmu(struct tb_switch *sw) int ret; /* - * If CL1 is enabled then we need to configure the TMU accuracy - * level to normal. Otherwise we keep the TMU running at the - * highest accuracy. + * If both routers at the end of the link are v2 we simply + * enable the enhanched uni-directional mode. That covers all + * the CL states. For v1 and before we need to use the normal + * rate to allow CL1 (when supported). Otherwise we keep the TMU + * running at the highest accuracy. */ - if (tb_switch_clx_is_enabled(sw, TB_CL1)) - ret = tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_NORMAL, true); - else - ret = tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, false); + ret = tb_switch_tmu_configure(sw, + TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI); + if (ret == -EOPNOTSUPP) { + if (tb_switch_clx_is_enabled(sw, TB_CL1)) + ret = tb_switch_tmu_configure(sw, + TB_SWITCH_TMU_MODE_LOWRES); + else + ret = tb_switch_tmu_configure(sw, + TB_SWITCH_TMU_MODE_HIFI_BI); + } if (ret) return ret; @@ -963,6 +986,12 @@ static void tb_scan_port(struct tb_port *port) if (tb_enable_tmu(sw)) tb_sw_warn(sw, "failed to enable TMU\n"); + /* + * Configuration valid needs to be set after the TMU has been + * enabled for the upstream port of the router so we do it here. + */ + tb_switch_configuration_valid(sw); + /* Scan upstream retimers */ tb_retimer_scan(upstream_port, true); @@ -2086,8 +2115,7 @@ static int tb_start(struct tb *tb) * To support highest CLx state, we set host router's TMU to * Normal mode. */ - tb_switch_tmu_configure(tb->root_switch, TB_SWITCH_TMU_RATE_NORMAL, - false); + tb_switch_tmu_configure(tb->root_switch, TB_SWITCH_TMU_MODE_LOWRES); /* Enable TMU if it is off */ tb_switch_tmu_enable(tb->root_switch); /* Full scan to discover devices added before the driver was loaded. */ @@ -2139,6 +2167,8 @@ static void tb_restore_children(struct tb_switch *sw) if (tb_enable_tmu(sw)) tb_sw_warn(sw, "failed to restore TMU configuration\n"); + tb_switch_configuration_valid(sw); + tb_switch_for_each_port(sw, port) { if (!tb_port_has_remote(port) && !port->xdomain) continue; diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 088033726648..68ab9b3c9580 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -73,44 +73,37 @@ enum tb_nvm_write_ops { #define USB4_SWITCH_MAX_DEPTH 5 /** - * enum tb_switch_tmu_rate - TMU refresh rate - * @TB_SWITCH_TMU_RATE_OFF: %0 (Disable Time Sync handshake) - * @TB_SWITCH_TMU_RATE_HIFI: %16 us time interval between successive - * transmission of the Delay Request TSNOS - * (Time Sync Notification Ordered Set) on a Link - * @TB_SWITCH_TMU_RATE_NORMAL: %1 ms time interval between successive - * transmission of the Delay Request TSNOS on - * a Link + * enum tb_switch_tmu_mode - TMU mode + * @TB_SWITCH_TMU_MODE_OFF: TMU is off + * @TB_SWITCH_TMU_MODE_LOWRES: Uni-directional, normal mode + * @TB_SWITCH_TMU_MODE_HIFI_UNI: Uni-directional, HiFi mode + * @TB_SWITCH_TMU_MODE_HIFI_BI: Bi-directional, HiFi mode + * @TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI: Enhanced Uni-directional, MedRes mode + * + * Ordering is based on TMU accuracy level (highest last). */ -enum tb_switch_tmu_rate { - TB_SWITCH_TMU_RATE_OFF = 0, - TB_SWITCH_TMU_RATE_HIFI = 16, - TB_SWITCH_TMU_RATE_NORMAL = 1000, +enum tb_switch_tmu_mode { + TB_SWITCH_TMU_MODE_OFF, + TB_SWITCH_TMU_MODE_LOWRES, + TB_SWITCH_TMU_MODE_HIFI_UNI, + TB_SWITCH_TMU_MODE_HIFI_BI, + TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI, }; /** - * struct tb_switch_tmu - Structure holding switch TMU configuration + * struct tb_switch_tmu - Structure holding router TMU configuration * @cap: Offset to the TMU capability (%0 if not found) * @has_ucap: Does the switch support uni-directional mode - * @rate: TMU refresh rate related to upstream switch. In case of root - * switch this holds the domain rate. Reflects the HW setting. - * @unidirectional: Is the TMU in uni-directional or bi-directional mode - * related to upstream switch. Don't care for root switch. - * Reflects the HW setting. - * @unidirectional_request: Is the new TMU mode: uni-directional or bi-directional - * that is requested to be set. Related to upstream switch. - * Don't care for root switch. - * @rate_request: TMU new refresh rate related to upstream switch that is - * requested to be set. In case of root switch, this holds - * the new domain rate that is requested to be set. + * @mode: TMU mode related to the upstream router. Reflects the HW + * setting. Don't care for host router. + * @mode_request: TMU mode requested to set. Related to upstream router. + * Don't care for host router. */ struct tb_switch_tmu { int cap; bool has_ucap; - enum tb_switch_tmu_rate rate; - bool unidirectional; - bool unidirectional_request; - enum tb_switch_tmu_rate rate_request; + enum tb_switch_tmu_mode mode; + enum tb_switch_tmu_mode mode_request; }; /** @@ -801,6 +794,7 @@ struct tb_switch *tb_switch_alloc(struct tb *tb, struct device *parent, struct tb_switch *tb_switch_alloc_safe_mode(struct tb *tb, struct device *parent, u64 route); int tb_switch_configure(struct tb_switch *sw); +int tb_switch_configuration_valid(struct tb_switch *sw); int tb_switch_add(struct tb_switch *sw); void tb_switch_remove(struct tb_switch *sw); void tb_switch_suspend(struct tb_switch *sw, bool runtime); @@ -975,19 +969,33 @@ int tb_switch_tmu_init(struct tb_switch *sw); int tb_switch_tmu_post_time(struct tb_switch *sw); int tb_switch_tmu_disable(struct tb_switch *sw); int tb_switch_tmu_enable(struct tb_switch *sw); -int tb_switch_tmu_configure(struct tb_switch *sw, enum tb_switch_tmu_rate rate, - bool unidirectional); +int tb_switch_tmu_configure(struct tb_switch *sw, enum tb_switch_tmu_mode mode); + +/** + * tb_switch_tmu_is_configured() - Is given TMU mode configured + * @sw: Router whose mode to check + * @mode: Mode to check + * + * Checks if given router TMU mode is configured to @mode. Note the + * router TMU might not be enabled to this mode. + */ +static inline bool tb_switch_tmu_is_configured(const struct tb_switch *sw, + enum tb_switch_tmu_mode mode) +{ + return sw->tmu.mode_request == mode; +} + /** * tb_switch_tmu_is_enabled() - Checks if the specified TMU mode is enabled * @sw: Router whose TMU mode to check * * Return true if hardware TMU configuration matches the requested - * configuration. + * configuration (and is not %TB_SWITCH_TMU_MODE_OFF). */ static inline bool tb_switch_tmu_is_enabled(const struct tb_switch *sw) { - return sw->tmu.rate == sw->tmu.rate_request && - sw->tmu.unidirectional == sw->tmu.unidirectional_request; + return sw->tmu.mode != TB_SWITCH_TMU_MODE_OFF && + sw->tmu.mode == sw->tmu.mode_request; } bool tb_port_clx_is_enabled(struct tb_port *port, unsigned int clx); @@ -1211,6 +1219,7 @@ static inline bool tb_switch_is_usb4(const struct tb_switch *sw) } int usb4_switch_setup(struct tb_switch *sw); +int usb4_switch_configuration_valid(struct tb_switch *sw); int usb4_switch_read_uid(struct tb_switch *sw, u64 *uid); int usb4_switch_drom_read(struct tb_switch *sw, unsigned int address, void *buf, size_t size); diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index 549cc79c7313..c95fc7fe7adf 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -252,11 +252,13 @@ enum usb4_switch_op { #define TMU_RTR_CS_3_LOCAL_TIME_NS_MASK GENMASK(15, 0) #define TMU_RTR_CS_3_TS_PACKET_INTERVAL_MASK GENMASK(31, 16) #define TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT 16 -#define TMU_RTR_CS_15 0xf +#define TMU_RTR_CS_15 0x0f #define TMU_RTR_CS_15_FREQ_AVG_MASK GENMASK(5, 0) #define TMU_RTR_CS_15_DELAY_AVG_MASK GENMASK(11, 6) #define TMU_RTR_CS_15_OFFSET_AVG_MASK GENMASK(17, 12) #define TMU_RTR_CS_15_ERROR_AVG_MASK GENMASK(23, 18) +#define TMU_RTR_CS_18 0x12 +#define TMU_RTR_CS_18_DELTA_AVG_CONST_MASK GENMASK(23, 16) #define TMU_RTR_CS_22 0x16 #define TMU_RTR_CS_24 0x18 #define TMU_RTR_CS_25 0x19 @@ -322,6 +324,14 @@ struct tb_regs_port_header { #define TMU_ADP_CS_3_UDM BIT(29) #define TMU_ADP_CS_6 0x06 #define TMU_ADP_CS_6_DTS BIT(1) +#define TMU_ADP_CS_8 0x08 +#define TMU_ADP_CS_8_REPL_TIMEOUT_MASK GENMASK(14, 0) +#define TMU_ADP_CS_8_EUDM BIT(15) +#define TMU_ADP_CS_8_REPL_THRESHOLD_MASK GENMASK(25, 16) +#define TMU_ADP_CS_9 0x09 +#define TMU_ADP_CS_9_REPL_N_MASK GENMASK(7, 0) +#define TMU_ADP_CS_9_DIRSWITCH_N_MASK GENMASK(15, 8) +#define TMU_ADP_CS_9_ADP_TS_INTERVAL_MASK GENMASK(31, 16) /* Lane adapter registers */ #define LANE_ADP_CS_0 0x00 diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index c926fb71c43d..1269f417515b 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -11,23 +11,63 @@ #include "tb.h" +static const unsigned int tmu_rates[] = { + [TB_SWITCH_TMU_MODE_OFF] = 0, + [TB_SWITCH_TMU_MODE_LOWRES] = 1000, + [TB_SWITCH_TMU_MODE_HIFI_UNI] = 16, + [TB_SWITCH_TMU_MODE_HIFI_BI] = 16, + [TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI] = 16, +}; + +const struct { + unsigned int freq_meas_window; + unsigned int avg_const; + unsigned int delta_avg_const; + unsigned int repl_timeout; + unsigned int repl_threshold; + unsigned int repl_n; + unsigned int dirswitch_n; +} tmu_params[] = { + [TB_SWITCH_TMU_MODE_OFF] = { }, + [TB_SWITCH_TMU_MODE_LOWRES] = { 30, 4, }, + [TB_SWITCH_TMU_MODE_HIFI_UNI] = { 800, 8, }, + [TB_SWITCH_TMU_MODE_HIFI_BI] = { 800, 8, }, + [TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI] = { + 800, 4, 0, 3125, 25, 128, 255, + }, +}; + +static const char *tmu_mode_name(enum tb_switch_tmu_mode mode) +{ + switch (mode) { + case TB_SWITCH_TMU_MODE_OFF: + return "off"; + case TB_SWITCH_TMU_MODE_LOWRES: + return "uni-directional, LowRes"; + case TB_SWITCH_TMU_MODE_HIFI_UNI: + return "uni-directional, HiFi"; + case TB_SWITCH_TMU_MODE_HIFI_BI: + return "bi-directional, HiFi"; + case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI: + return "enhanced uni-directional, MedRes"; + default: + return "unknown"; + } +} + +static bool tb_switch_tmu_enhanced_is_supported(const struct tb_switch *sw) +{ + return usb4_switch_version(sw) > 1; +} + static int tb_switch_set_tmu_mode_params(struct tb_switch *sw, - enum tb_switch_tmu_rate rate) + enum tb_switch_tmu_mode mode) { - u32 freq_meas_wind[2] = { 30, 800 }; - u32 avg_const[2] = { 4, 8 }; u32 freq, avg, val; int ret; - if (rate == TB_SWITCH_TMU_RATE_NORMAL) { - freq = freq_meas_wind[0]; - avg = avg_const[0]; - } else if (rate == TB_SWITCH_TMU_RATE_HIFI) { - freq = freq_meas_wind[1]; - avg = avg_const[1]; - } else { - return 0; - } + freq = tmu_params[mode].freq_meas_window; + avg = tmu_params[mode].avg_const; ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, sw->tmu.cap + TMU_RTR_CS_0, 1); @@ -56,37 +96,30 @@ static int tb_switch_set_tmu_mode_params(struct tb_switch *sw, FIELD_PREP(TMU_RTR_CS_15_OFFSET_AVG_MASK, avg) | FIELD_PREP(TMU_RTR_CS_15_ERROR_AVG_MASK, avg); - return tb_sw_write(sw, &val, TB_CFG_SWITCH, - sw->tmu.cap + TMU_RTR_CS_15, 1); -} - -static const char *tb_switch_tmu_mode_name(const struct tb_switch *sw) -{ - bool root_switch = !tb_route(sw); + ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, + sw->tmu.cap + TMU_RTR_CS_15, 1); + if (ret) + return ret; - switch (sw->tmu.rate) { - case TB_SWITCH_TMU_RATE_OFF: - return "off"; + if (tb_switch_tmu_enhanced_is_supported(sw)) { + u32 delta_avg = tmu_params[mode].delta_avg_const; - case TB_SWITCH_TMU_RATE_HIFI: - /* Root switch does not have upstream directionality */ - if (root_switch) - return "HiFi"; - if (sw->tmu.unidirectional) - return "uni-directional, HiFi"; - return "bi-directional, HiFi"; + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, + sw->tmu.cap + TMU_RTR_CS_18, 1); + if (ret) + return ret; - case TB_SWITCH_TMU_RATE_NORMAL: - if (root_switch) - return "normal"; - return "uni-directional, normal"; + val &= ~TMU_RTR_CS_18_DELTA_AVG_CONST_MASK; + val |= FIELD_PREP(TMU_RTR_CS_18_DELTA_AVG_CONST_MASK, delta_avg); - default: - return "unknown"; + ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, + sw->tmu.cap + TMU_RTR_CS_18, 1); } + + return ret; } -static bool tb_switch_tmu_ucap_supported(struct tb_switch *sw) +static bool tb_switch_tmu_ucap_is_supported(struct tb_switch *sw) { int ret; u32 val; @@ -182,6 +215,103 @@ static bool tb_port_tmu_is_unidirectional(struct tb_port *port) return val & TMU_ADP_CS_3_UDM; } +static bool tb_port_tmu_is_enhanced(struct tb_port *port) +{ + int ret; + u32 val; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_tmu + TMU_ADP_CS_8, 1); + if (ret) + return false; + + return val & TMU_ADP_CS_8_EUDM; +} + +/* Can be called to non-v2 lane adapters too */ +static int tb_port_tmu_enhanced_enable(struct tb_port *port, bool enable) +{ + int ret; + u32 val; + + if (!tb_switch_tmu_enhanced_is_supported(port->sw)) + return 0; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_tmu + TMU_ADP_CS_8, 1); + if (ret) + return ret; + + if (enable) + val |= TMU_ADP_CS_8_EUDM; + else + val &= ~TMU_ADP_CS_8_EUDM; + + return tb_port_write(port, &val, TB_CFG_PORT, + port->cap_tmu + TMU_ADP_CS_8, 1); +} + +static int tb_port_set_tmu_mode_params(struct tb_port *port, + enum tb_switch_tmu_mode mode) +{ + u32 repl_timeout, repl_threshold, repl_n, dirswitch_n, val; + int ret; + + repl_timeout = tmu_params[mode].repl_timeout; + repl_threshold = tmu_params[mode].repl_threshold; + repl_n = tmu_params[mode].repl_n; + dirswitch_n = tmu_params[mode].dirswitch_n; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_tmu + TMU_ADP_CS_8, 1); + if (ret) + return ret; + + val &= ~TMU_ADP_CS_8_REPL_TIMEOUT_MASK; + val &= ~TMU_ADP_CS_8_REPL_THRESHOLD_MASK; + val |= FIELD_PREP(TMU_ADP_CS_8_REPL_TIMEOUT_MASK, repl_timeout); + val |= FIELD_PREP(TMU_ADP_CS_8_REPL_THRESHOLD_MASK, repl_threshold); + + ret = tb_port_write(port, &val, TB_CFG_PORT, + port->cap_tmu + TMU_ADP_CS_8, 1); + if (ret) + return ret; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_tmu + TMU_ADP_CS_9, 1); + if (ret) + return ret; + + val &= ~TMU_ADP_CS_9_REPL_N_MASK; + val &= ~TMU_ADP_CS_9_DIRSWITCH_N_MASK; + val |= FIELD_PREP(TMU_ADP_CS_9_REPL_N_MASK, repl_n); + val |= FIELD_PREP(TMU_ADP_CS_9_DIRSWITCH_N_MASK, dirswitch_n); + + return tb_port_write(port, &val, TB_CFG_PORT, + port->cap_tmu + TMU_ADP_CS_9, 1); +} + +/* Can be called to non-v2 lane adapters too */ +static int tb_port_tmu_rate_write(struct tb_port *port, int rate) +{ + int ret; + u32 val; + + if (!tb_switch_tmu_enhanced_is_supported(port->sw)) + return 0; + + ret = tb_port_read(port, &val, TB_CFG_PORT, + port->cap_tmu + TMU_ADP_CS_9, 1); + if (ret) + return ret; + + val &= ~TMU_ADP_CS_9_ADP_TS_INTERVAL_MASK; + val |= FIELD_PREP(TMU_ADP_CS_9_ADP_TS_INTERVAL_MASK, rate); + + return tb_port_write(port, &val, TB_CFG_PORT, + port->cap_tmu + TMU_ADP_CS_9, 1); +} + static int tb_port_tmu_time_sync(struct tb_port *port, bool time_sync) { u32 val = time_sync ? TMU_ADP_CS_6_DTS : 0; @@ -224,6 +354,50 @@ static int tb_switch_tmu_set_time_disruption(struct tb_switch *sw, bool set) return tb_sw_write(sw, &val, TB_CFG_SWITCH, offset, 1); } +static int tmu_mode_init(struct tb_switch *sw) +{ + bool enhanced, ucap; + int ret, rate; + + ucap = tb_switch_tmu_ucap_is_supported(sw); + if (ucap) + tb_sw_dbg(sw, "TMU: supports uni-directional mode\n"); + enhanced = tb_switch_tmu_enhanced_is_supported(sw); + if (enhanced) + tb_sw_dbg(sw, "TMU: supports enhanced uni-directional mode\n"); + + ret = tb_switch_tmu_rate_read(sw); + if (ret < 0) + return ret; + rate = ret; + + /* Off by default */ + sw->tmu.mode = TB_SWITCH_TMU_MODE_OFF; + + if (tb_route(sw)) { + struct tb_port *up = tb_upstream_port(sw); + + if (enhanced && tb_port_tmu_is_enhanced(up)) { + sw->tmu.mode = TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI; + } else if (ucap && tb_port_tmu_is_unidirectional(up)) { + if (tmu_rates[TB_SWITCH_TMU_MODE_LOWRES] == rate) + sw->tmu.mode = TB_SWITCH_TMU_MODE_LOWRES; + else if (tmu_rates[TB_SWITCH_TMU_MODE_LOWRES] == rate) + sw->tmu.mode = TB_SWITCH_TMU_MODE_HIFI_UNI; + } else if (rate) { + sw->tmu.mode = TB_SWITCH_TMU_MODE_HIFI_BI; + } + } else if (rate) { + sw->tmu.mode = TB_SWITCH_TMU_MODE_HIFI_BI; + } + + /* Update the initial request to match the current mode */ + sw->tmu.mode_request = sw->tmu.mode; + sw->tmu.has_ucap = ucap; + + return 0; +} + /** * tb_switch_tmu_init() - Initialize switch TMU structures * @sw: Switch to initialized @@ -252,27 +426,11 @@ int tb_switch_tmu_init(struct tb_switch *sw) port->cap_tmu = cap; } - ret = tb_switch_tmu_rate_read(sw); - if (ret < 0) + ret = tmu_mode_init(sw); + if (ret) return ret; - sw->tmu.rate = ret; - - sw->tmu.has_ucap = tb_switch_tmu_ucap_supported(sw); - if (sw->tmu.has_ucap) { - tb_sw_dbg(sw, "TMU: supports uni-directional mode\n"); - - if (tb_route(sw)) { - struct tb_port *up = tb_upstream_port(sw); - - sw->tmu.unidirectional = - tb_port_tmu_is_unidirectional(up); - } - } else { - sw->tmu.unidirectional = false; - } - - tb_sw_dbg(sw, "TMU: current mode: %s\n", tb_switch_tmu_mode_name(sw)); + tb_sw_dbg(sw, "TMU: current mode: %s\n", tmu_mode_name(sw->tmu.mode)); return 0; } @@ -375,6 +533,23 @@ out: return ret; } +static int disable_enhanced(struct tb_port *up, struct tb_port *down) +{ + int ret; + + /* + * Router may already been disconnected so ignore errors on the + * upstream port. + */ + tb_port_tmu_rate_write(up, 0); + tb_port_tmu_enhanced_enable(up, false); + + ret = tb_port_tmu_rate_write(down, 0); + if (ret) + return ret; + return tb_port_tmu_enhanced_enable(down, false); +} + /** * tb_switch_tmu_disable() - Disable TMU of a switch * @sw: Switch whose TMU to disable @@ -384,11 +559,10 @@ out: int tb_switch_tmu_disable(struct tb_switch *sw) { /* Already disabled? */ - if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF) + if (sw->tmu.mode == TB_SWITCH_TMU_MODE_OFF) return 0; if (tb_route(sw)) { - bool unidirectional = sw->tmu.unidirectional; struct tb_port *down, *up; int ret; @@ -405,33 +579,46 @@ int tb_switch_tmu_disable(struct tb_switch *sw) * uni-directional mode and we don't want to change it's TMU * mode. */ - tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF); + tb_switch_tmu_rate_write(sw, tmu_rates[TB_SWITCH_TMU_MODE_OFF]); tb_port_tmu_time_sync_disable(up); ret = tb_port_tmu_time_sync_disable(down); if (ret) return ret; - if (unidirectional) { + switch (sw->tmu.mode) { + case TB_SWITCH_TMU_MODE_LOWRES: + case TB_SWITCH_TMU_MODE_HIFI_UNI: /* The switch may be unplugged so ignore any errors */ tb_port_tmu_unidirectional_disable(up); ret = tb_port_tmu_unidirectional_disable(down); if (ret) return ret; + break; + + case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI: + ret = disable_enhanced(up, down); + if (ret) + return ret; + break; + + default: + break; } } else { - tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF); + tb_switch_tmu_rate_write(sw, tmu_rates[TB_SWITCH_TMU_MODE_OFF]); } - sw->tmu.unidirectional = false; - sw->tmu.rate = TB_SWITCH_TMU_RATE_OFF; + sw->tmu.mode = TB_SWITCH_TMU_MODE_OFF; tb_sw_dbg(sw, "TMU: disabled\n"); return 0; } -static void tb_switch_tmu_off(struct tb_switch *sw, bool unidirectional) +/* Called only when there is failure enabling requested mode */ +static void tb_switch_tmu_off(struct tb_switch *sw) { + unsigned int rate = tmu_rates[TB_SWITCH_TMU_MODE_OFF]; struct tb_port *down, *up; down = tb_switch_downstream_port(sw); @@ -445,20 +632,30 @@ static void tb_switch_tmu_off(struct tb_switch *sw, bool unidirectional) */ tb_port_tmu_time_sync_disable(down); tb_port_tmu_time_sync_disable(up); - if (unidirectional) - tb_switch_tmu_rate_write(tb_switch_parent(sw), - TB_SWITCH_TMU_RATE_OFF); - else - tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF); - tb_switch_set_tmu_mode_params(sw, sw->tmu.rate); + switch (sw->tmu.mode_request) { + case TB_SWITCH_TMU_MODE_LOWRES: + case TB_SWITCH_TMU_MODE_HIFI_UNI: + tb_switch_tmu_rate_write(tb_switch_parent(sw), rate); + break; + case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI: + disable_enhanced(up, down); + break; + default: + break; + } + + /* Always set the rate to 0 */ + tb_switch_tmu_rate_write(sw, rate); + + tb_switch_set_tmu_mode_params(sw, sw->tmu.mode); tb_port_tmu_unidirectional_disable(down); tb_port_tmu_unidirectional_disable(up); } /* * This function is called when the previous TMU mode was - * TB_SWITCH_TMU_RATE_OFF. + * TB_SWITCH_TMU_MODE_OFF. */ static int tb_switch_tmu_enable_bidirectional(struct tb_switch *sw) { @@ -476,7 +673,7 @@ static int tb_switch_tmu_enable_bidirectional(struct tb_switch *sw) if (ret) goto out; - ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI); + ret = tb_switch_tmu_rate_write(sw, tmu_rates[TB_SWITCH_TMU_MODE_HIFI_BI]); if (ret) goto out; @@ -491,7 +688,7 @@ static int tb_switch_tmu_enable_bidirectional(struct tb_switch *sw) return 0; out: - tb_switch_tmu_off(sw, false); + tb_switch_tmu_off(sw); return ret; } @@ -522,7 +719,7 @@ static int tb_switch_tmu_disable_objections(struct tb_switch *sw) /* * This function is called when the previous TMU mode was - * TB_SWITCH_TMU_RATE_OFF. + * TB_SWITCH_TMU_MODE_OFF. */ static int tb_switch_tmu_enable_unidirectional(struct tb_switch *sw) { @@ -532,11 +729,11 @@ static int tb_switch_tmu_enable_unidirectional(struct tb_switch *sw) up = tb_upstream_port(sw); down = tb_switch_downstream_port(sw); ret = tb_switch_tmu_rate_write(tb_switch_parent(sw), - sw->tmu.rate_request); + tmu_rates[sw->tmu.mode_request]); if (ret) return ret; - ret = tb_switch_set_tmu_mode_params(sw, sw->tmu.rate_request); + ret = tb_switch_set_tmu_mode_params(sw, sw->tmu.mode_request); if (ret) return ret; @@ -559,12 +756,62 @@ static int tb_switch_tmu_enable_unidirectional(struct tb_switch *sw) return 0; out: - tb_switch_tmu_off(sw, true); + tb_switch_tmu_off(sw); + return ret; +} + +/* + * This function is called when the previous TMU mode was + * TB_SWITCH_TMU_RATE_OFF. + */ +static int tb_switch_tmu_enable_enhanced(struct tb_switch *sw) +{ + unsigned int rate = tmu_rates[sw->tmu.mode_request]; + struct tb_port *up, *down; + int ret; + + /* Router specific parameters first */ + ret = tb_switch_set_tmu_mode_params(sw, sw->tmu.mode_request); + if (ret) + return ret; + + up = tb_upstream_port(sw); + down = tb_switch_downstream_port(sw); + + ret = tb_port_set_tmu_mode_params(up, sw->tmu.mode_request); + if (ret) + goto out; + + ret = tb_port_tmu_rate_write(up, rate); + if (ret) + goto out; + + ret = tb_port_tmu_enhanced_enable(up, true); + if (ret) + goto out; + + ret = tb_port_set_tmu_mode_params(down, sw->tmu.mode_request); + if (ret) + goto out; + + ret = tb_port_tmu_rate_write(down, rate); + if (ret) + goto out; + + ret = tb_port_tmu_enhanced_enable(down, true); + if (ret) + goto out; + + return 0; + +out: + tb_switch_tmu_off(sw); return ret; } static void tb_switch_tmu_change_mode_prev(struct tb_switch *sw) { + unsigned int rate = tmu_rates[sw->tmu.mode]; struct tb_port *down, *up; down = tb_switch_downstream_port(sw); @@ -575,42 +822,97 @@ static void tb_switch_tmu_change_mode_prev(struct tb_switch *sw) * In case of additional failures in the functions below, * ignore them since the caller shall already report a failure. */ - tb_port_tmu_set_unidirectional(down, sw->tmu.unidirectional); - if (sw->tmu.unidirectional_request) - tb_switch_tmu_rate_write(tb_switch_parent(sw), sw->tmu.rate); - else - tb_switch_tmu_rate_write(sw, sw->tmu.rate); + switch (sw->tmu.mode) { + case TB_SWITCH_TMU_MODE_LOWRES: + case TB_SWITCH_TMU_MODE_HIFI_UNI: + tb_port_tmu_set_unidirectional(down, true); + tb_switch_tmu_rate_write(tb_switch_parent(sw), rate); + break; + + case TB_SWITCH_TMU_MODE_HIFI_BI: + tb_port_tmu_set_unidirectional(down, false); + tb_switch_tmu_rate_write(sw, rate); + break; - tb_switch_set_tmu_mode_params(sw, sw->tmu.rate); - tb_port_tmu_set_unidirectional(up, sw->tmu.unidirectional); + default: + break; + } + + tb_switch_set_tmu_mode_params(sw, sw->tmu.mode); + + switch (sw->tmu.mode) { + case TB_SWITCH_TMU_MODE_LOWRES: + case TB_SWITCH_TMU_MODE_HIFI_UNI: + tb_port_tmu_set_unidirectional(up, true); + break; + + case TB_SWITCH_TMU_MODE_HIFI_BI: + tb_port_tmu_set_unidirectional(up, false); + break; + + default: + break; + } } static int tb_switch_tmu_change_mode(struct tb_switch *sw) { + unsigned int rate = tmu_rates[sw->tmu.mode_request]; struct tb_port *up, *down; int ret; up = tb_upstream_port(sw); down = tb_switch_downstream_port(sw); - ret = tb_port_tmu_set_unidirectional(down, sw->tmu.unidirectional_request); - if (ret) - goto out; - if (sw->tmu.unidirectional_request) - ret = tb_switch_tmu_rate_write(tb_switch_parent(sw), - sw->tmu.rate_request); - else - ret = tb_switch_tmu_rate_write(sw, sw->tmu.rate_request); - if (ret) - return ret; + /* Program the upstream router downstream facing lane adapter */ + switch (sw->tmu.mode_request) { + case TB_SWITCH_TMU_MODE_LOWRES: + case TB_SWITCH_TMU_MODE_HIFI_UNI: + ret = tb_port_tmu_set_unidirectional(down, true); + if (ret) + goto out; + ret = tb_switch_tmu_rate_write(tb_switch_parent(sw), rate); + if (ret) + goto out; + break; + + case TB_SWITCH_TMU_MODE_HIFI_BI: + ret = tb_port_tmu_set_unidirectional(down, false); + if (ret) + goto out; + ret = tb_switch_tmu_rate_write(sw, rate); + if (ret) + goto out; + break; - ret = tb_switch_set_tmu_mode_params(sw, sw->tmu.rate_request); + default: + /* Not allowed to change modes from other than above */ + return -EINVAL; + } + + ret = tb_switch_set_tmu_mode_params(sw, sw->tmu.mode_request); if (ret) return ret; - ret = tb_port_tmu_set_unidirectional(up, sw->tmu.unidirectional_request); - if (ret) - goto out; + /* Program the new mode and the downstream router lane adapter */ + switch (sw->tmu.mode_request) { + case TB_SWITCH_TMU_MODE_LOWRES: + case TB_SWITCH_TMU_MODE_HIFI_UNI: + ret = tb_port_tmu_set_unidirectional(up, true); + if (ret) + goto out; + break; + + case TB_SWITCH_TMU_MODE_HIFI_BI: + ret = tb_port_tmu_set_unidirectional(up, false); + if (ret) + goto out; + break; + + default: + /* Not allowed to change modes from other than above */ + return -EINVAL; + } ret = tb_port_tmu_time_sync_enable(down); if (ret) @@ -637,13 +939,14 @@ out: */ int tb_switch_tmu_enable(struct tb_switch *sw) { - bool unidirectional = sw->tmu.unidirectional_request; int ret; if (tb_switch_tmu_is_enabled(sw)) return 0; - if (tb_switch_is_titan_ridge(sw) && unidirectional) { + if (tb_switch_is_titan_ridge(sw) && + (sw->tmu.mode_request == TB_SWITCH_TMU_MODE_LOWRES || + sw->tmu.mode_request == TB_SWITCH_TMU_MODE_HIFI_UNI)) { ret = tb_switch_tmu_disable_objections(sw); if (ret) return ret; @@ -659,19 +962,30 @@ int tb_switch_tmu_enable(struct tb_switch *sw) * HiFi-Uni/HiFi-BiDir/Normal-Uni or from Normal-Uni to * HiFi-Uni. */ - if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF) { - if (unidirectional) + if (sw->tmu.mode == TB_SWITCH_TMU_MODE_OFF) { + switch (sw->tmu.mode_request) { + case TB_SWITCH_TMU_MODE_LOWRES: + case TB_SWITCH_TMU_MODE_HIFI_UNI: ret = tb_switch_tmu_enable_unidirectional(sw); - else + break; + + case TB_SWITCH_TMU_MODE_HIFI_BI: ret = tb_switch_tmu_enable_bidirectional(sw); - if (ret) - return ret; - } else if (sw->tmu.rate == TB_SWITCH_TMU_RATE_NORMAL) { + break; + case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI: + ret = tb_switch_tmu_enable_enhanced(sw); + break; + default: + ret = -EINVAL; + break; + } + } else if (sw->tmu.mode == TB_SWITCH_TMU_MODE_LOWRES || + sw->tmu.mode == TB_SWITCH_TMU_MODE_HIFI_UNI || + sw->tmu.mode == TB_SWITCH_TMU_MODE_HIFI_BI) { ret = tb_switch_tmu_change_mode(sw); - if (ret) - return ret; + } else { + ret = -EINVAL; } - sw->tmu.unidirectional = unidirectional; } else { /* * Host router port configurations are written as @@ -679,35 +993,68 @@ int tb_switch_tmu_enable(struct tb_switch *sw) * of the child node - see above. * Here only the host router' rate configuration is written. */ - ret = tb_switch_tmu_rate_write(sw, sw->tmu.rate_request); - if (ret) - return ret; + ret = tb_switch_tmu_rate_write(sw, tmu_rates[sw->tmu.mode_request]); } - sw->tmu.rate = sw->tmu.rate_request; + if (ret) { + tb_sw_warn(sw, "TMU: failed to enable mode %s: %d\n", + tmu_mode_name(sw->tmu.mode_request), ret); + } else { + sw->tmu.mode = sw->tmu.mode_request; + tb_sw_dbg(sw, "TMU: mode set to: %s\n", tmu_mode_name(sw->tmu.mode)); + } - tb_sw_dbg(sw, "TMU: mode set to: %s\n", tb_switch_tmu_mode_name(sw)); return tb_switch_tmu_set_time_disruption(sw, false); } /** - * tb_switch_tmu_configure() - Configure the TMU rate and directionality + * tb_switch_tmu_configure() - Configure the TMU mode * @sw: Router whose mode to change - * @rate: Rate to configure Off/Normal/HiFi - * @unidirectional: If uni-directional (bi-directional otherwise) + * @mode: Mode to configure * - * Selects the rate of the TMU and directionality (uni-directional or - * bi-directional). Must be called before tb_switch_tmu_enable(). + * Selects the TMU mode that is enabled when tb_switch_tmu_enable() is + * next called. * - * Returns %0 in success and negative errno otherwise. + * Returns %0 in success and negative errno otherwise. Specifically + * returns %-EOPNOTSUPP if the requested mode is not possible (not + * supported by the router and/or topology). */ -int tb_switch_tmu_configure(struct tb_switch *sw, enum tb_switch_tmu_rate rate, - bool unidirectional) +int tb_switch_tmu_configure(struct tb_switch *sw, enum tb_switch_tmu_mode mode) { - if (unidirectional && !sw->tmu.has_ucap) + switch (mode) { + case TB_SWITCH_TMU_MODE_OFF: + break; + + case TB_SWITCH_TMU_MODE_LOWRES: + case TB_SWITCH_TMU_MODE_HIFI_UNI: + if (!sw->tmu.has_ucap) + return -EOPNOTSUPP; + break; + + case TB_SWITCH_TMU_MODE_HIFI_BI: + break; + + case TB_SWITCH_TMU_MODE_MEDRES_ENHANCED_UNI: { + const struct tb_switch *parent_sw = tb_switch_parent(sw); + + if (!parent_sw || !tb_switch_tmu_enhanced_is_supported(parent_sw)) + return -EOPNOTSUPP; + if (!tb_switch_tmu_enhanced_is_supported(sw)) + return -EOPNOTSUPP; + + break; + } + + default: + tb_sw_warn(sw, "TMU: unsupported mode %u\n", mode); return -EINVAL; + } + + if (sw->tmu.mode_request != mode) { + tb_sw_dbg(sw, "TMU: mode change %s -> %s requested\n", + tmu_mode_name(sw->tmu.mode), tmu_mode_name(mode)); + sw->tmu.mode_request = mode; + } - sw->tmu.unidirectional_request = unidirectional; - sw->tmu.rate_request = rate; return 0; } diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index d203b6594672..fc213b038dfa 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -232,6 +232,9 @@ static bool link_is_usb4(struct tb_port *port) * is not available for some reason (like that there is Thunderbolt 3 * switch upstream) then the internal xHCI controller is enabled * instead. + * + * This does not set the configuration valid bit of the router. To do + * that call usb4_switch_configuration_valid(). */ int usb4_switch_setup(struct tb_switch *sw) { @@ -288,7 +291,33 @@ int usb4_switch_setup(struct tb_switch *sw) /* TBT3 supported by the CM */ val |= ROUTER_CS_5_C3S; - /* Tunneling configuration is ready now */ + + return tb_sw_write(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1); +} + +/** + * usb4_switch_configuration_valid() - Set tunneling configuration to be valid + * @sw: USB4 router + * + * Sets configuration valid bit for the router. Must be called before + * any tunnels can be set through the router and after + * usb4_switch_setup() has been called. Can be called to host and device + * routers (does nothing for the latter). + * + * Returns %0 in success and negative errno otherwise. + */ +int usb4_switch_configuration_valid(struct tb_switch *sw) +{ + u32 val; + int ret; + + if (!tb_route(sw)) + return 0; + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1); + if (ret) + return ret; + val |= ROUTER_CS_5_CV; ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, ROUTER_CS_5, 1); -- cgit v1.2.3 From fd4d58d1fef9ae9b0ee235eaad73d2e0a6a73025 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Sun, 6 Nov 2022 13:07:43 +0200 Subject: thunderbolt: Enable CL2 low power state For USB4 v2 routers we can also enable CL2 which allows better power savings and thermal management than CL0s and CL1. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/clx.c | 31 +++++++++++++++++++------------ drivers/thunderbolt/tb.c | 9 ++++++--- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/drivers/thunderbolt/clx.c b/drivers/thunderbolt/clx.c index 604cceb23659..13d217ae98e6 100644 --- a/drivers/thunderbolt/clx.c +++ b/drivers/thunderbolt/clx.c @@ -17,17 +17,22 @@ MODULE_PARM_DESC(clx, "allow low power states on the high-speed lanes (default: static const char *clx_name(unsigned int clx) { - if (!clx) - return "disabled"; - - if (clx & TB_CL2) + switch (clx) { + case TB_CL0S | TB_CL1 | TB_CL2: return "CL0s/CL1/CL2"; - if (clx & TB_CL1) + case TB_CL1 | TB_CL2: + return "CL1/CL2"; + case TB_CL0S | TB_CL2: + return "CL0s/CL2"; + case TB_CL0S | TB_CL1: return "CL0s/CL1"; - if (clx & TB_CL0S) + case TB_CL0S: return "CL0s"; - - return "unknown"; + case 0: + return "disabled"; + default: + return "unknown"; + } } static int tb_port_pm_secondary_set(struct tb_port *port, bool secondary) @@ -104,6 +109,8 @@ static int tb_port_clx_set(struct tb_port *port, unsigned int clx, bool enable) mask |= LANE_ADP_CS_1_CL0S_ENABLE; if (clx & TB_CL1) mask |= LANE_ADP_CS_1_CL1_ENABLE; + if (clx & TB_CL2) + mask |= LANE_ADP_CS_1_CL2_ENABLE; if (!mask) return -EOPNOTSUPP; @@ -291,8 +298,6 @@ bool tb_switch_clx_is_supported(const struct tb_switch *sw) static bool validate_mask(unsigned int clx) { /* Previous states need to be enabled */ - if (clx & TB_CL2) - return (clx & (TB_CL0S | TB_CL1)) == (TB_CL0S | TB_CL1); if (clx & TB_CL1) return (clx & TB_CL0S) == TB_CL0S; return true; @@ -331,8 +336,10 @@ int tb_switch_clx_enable(struct tb_switch *sw, unsigned int clx) !tb_switch_clx_is_supported(sw)) return 0; - /* CL2 is not yet supported */ - if (clx & TB_CL2) + /* Only support CL2 for v2 routers */ + if ((clx & TB_CL2) && + (usb4_switch_version(parent_sw) < 2 || + usb4_switch_version(sw) < 2)) return -EOPNOTSUPP; ret = tb_switch_pm_secondary_resolve(sw); diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index ff034975a87e..79efc85db38b 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -244,6 +244,7 @@ static void tb_discover_dp_resources(struct tb *tb) static int tb_enable_clx(struct tb_switch *sw) { struct tb_cm *tcm = tb_priv(sw->tb); + unsigned int clx = TB_CL0S | TB_CL1; const struct tb_tunnel *tunnel; int ret; @@ -275,10 +276,12 @@ static int tb_enable_clx(struct tb_switch *sw) } /* - * CL0s and CL1 are enabled and supported together. - * Silently ignore CLx enabling in case CLx is not supported. + * Initially try with CL2. If that's not supported by the + * topology try with CL0s and CL1 and then give up. */ - ret = tb_switch_clx_enable(sw, TB_CL0S | TB_CL1); + ret = tb_switch_clx_enable(sw, clx | TB_CL2); + if (ret == -EOPNOTSUPP) + ret = tb_switch_clx_enable(sw, clx); return ret == -EOPNOTSUPP ? 0 : ret; } -- cgit v1.2.3 From 8d73f6b8e0487ac0ed4acd883a6788b2492a5692 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 1 Feb 2023 13:21:37 +0200 Subject: thunderbolt: Make bandwidth allocation mode function names consistent Make sure the DisplayPort bandwidth allocation mode function names are consistent with the existing ones, such as USB3. No functional changes. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb.c | 10 +++++----- drivers/thunderbolt/tb.h | 15 ++++++++------- drivers/thunderbolt/tunnel.c | 41 +++++++++++++++++++++-------------------- drivers/thunderbolt/usb4.c | 32 ++++++++++++++++++-------------- 4 files changed, 52 insertions(+), 46 deletions(-) diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 79efc85db38b..62b26b7998fd 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -131,7 +131,7 @@ tb_attach_bandwidth_group(struct tb_cm *tcm, struct tb_port *in, static void tb_discover_bandwidth_group(struct tb_cm *tcm, struct tb_port *in, struct tb_port *out) { - if (usb4_dp_port_bw_mode_enabled(in)) { + if (usb4_dp_port_bandwidth_mode_enabled(in)) { int index, i; index = usb4_dp_port_group_id(in); @@ -1169,7 +1169,7 @@ tb_recalc_estimated_bandwidth_for_group(struct tb_bandwidth_group *group) struct tb_tunnel *tunnel; struct tb_port *out; - if (!usb4_dp_port_bw_mode_enabled(in)) + if (!usb4_dp_port_bandwidth_mode_enabled(in)) continue; tunnel = tb_find_tunnel(tb, TB_TUNNEL_DP, in, NULL); @@ -1217,7 +1217,7 @@ tb_recalc_estimated_bandwidth_for_group(struct tb_bandwidth_group *group) else estimated_bw = estimated_up; - if (usb4_dp_port_set_estimated_bw(in, estimated_bw)) + if (usb4_dp_port_set_estimated_bandwidth(in, estimated_bw)) tb_port_warn(in, "failed to update estimated bandwidth\n"); } @@ -1912,12 +1912,12 @@ static void tb_handle_dp_bandwidth_request(struct work_struct *work) tb_port_dbg(in, "handling bandwidth allocation request\n"); - if (!usb4_dp_port_bw_mode_enabled(in)) { + if (!usb4_dp_port_bandwidth_mode_enabled(in)) { tb_port_warn(in, "bandwidth allocation mode not enabled\n"); goto unlock; } - ret = usb4_dp_port_requested_bw(in); + ret = usb4_dp_port_requested_bandwidth(in); if (ret < 0) { if (ret == -ENODATA) tb_port_dbg(in, "no bandwidth request active\n"); diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index 68ab9b3c9580..57a9b272cb94 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -1292,19 +1292,20 @@ int usb4_usb3_port_release_bandwidth(struct tb_port *port, int *upstream_bw, int *downstream_bw); int usb4_dp_port_set_cm_id(struct tb_port *port, int cm_id); -bool usb4_dp_port_bw_mode_supported(struct tb_port *port); -bool usb4_dp_port_bw_mode_enabled(struct tb_port *port); -int usb4_dp_port_set_cm_bw_mode_supported(struct tb_port *port, bool supported); +bool usb4_dp_port_bandwidth_mode_supported(struct tb_port *port); +bool usb4_dp_port_bandwidth_mode_enabled(struct tb_port *port); +int usb4_dp_port_set_cm_bandwidth_mode_supported(struct tb_port *port, + bool supported); int usb4_dp_port_group_id(struct tb_port *port); int usb4_dp_port_set_group_id(struct tb_port *port, int group_id); int usb4_dp_port_nrd(struct tb_port *port, int *rate, int *lanes); int usb4_dp_port_set_nrd(struct tb_port *port, int rate, int lanes); int usb4_dp_port_granularity(struct tb_port *port); int usb4_dp_port_set_granularity(struct tb_port *port, int granularity); -int usb4_dp_port_set_estimated_bw(struct tb_port *port, int bw); -int usb4_dp_port_allocated_bw(struct tb_port *port); -int usb4_dp_port_allocate_bw(struct tb_port *port, int bw); -int usb4_dp_port_requested_bw(struct tb_port *port); +int usb4_dp_port_set_estimated_bandwidth(struct tb_port *port, int bw); +int usb4_dp_port_allocated_bandwidth(struct tb_port *port); +int usb4_dp_port_allocate_bandwidth(struct tb_port *port, int bw); +int usb4_dp_port_requested_bandwidth(struct tb_port *port); int usb4_pci_port_set_ext_encapsulation(struct tb_port *port, bool enable); diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index c8ca0a41dac8..224f4265b755 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -641,7 +641,7 @@ static int tb_dp_xchg_caps(struct tb_tunnel *tunnel) in->cap_adap + DP_REMOTE_CAP, 1); } -static int tb_dp_bw_alloc_mode_enable(struct tb_tunnel *tunnel) +static int tb_dp_bandwidth_alloc_mode_enable(struct tb_tunnel *tunnel) { int ret, estimated_bw, granularity, tmp; struct tb_port *out = tunnel->dst_port; @@ -653,7 +653,7 @@ static int tb_dp_bw_alloc_mode_enable(struct tb_tunnel *tunnel) if (!bw_alloc_mode) return 0; - ret = usb4_dp_port_set_cm_bw_mode_supported(in, true); + ret = usb4_dp_port_set_cm_bandwidth_mode_supported(in, true); if (ret) return ret; @@ -717,12 +717,12 @@ static int tb_dp_bw_alloc_mode_enable(struct tb_tunnel *tunnel) tb_port_dbg(in, "estimated bandwidth %d Mb/s\n", estimated_bw); - ret = usb4_dp_port_set_estimated_bw(in, estimated_bw); + ret = usb4_dp_port_set_estimated_bandwidth(in, estimated_bw); if (ret) return ret; /* Initial allocation should be 0 according the spec */ - ret = usb4_dp_port_allocate_bw(in, 0); + ret = usb4_dp_port_allocate_bandwidth(in, 0); if (ret) return ret; @@ -744,7 +744,7 @@ static int tb_dp_init(struct tb_tunnel *tunnel) if (!tb_switch_is_usb4(sw)) return 0; - if (!usb4_dp_port_bw_mode_supported(in)) + if (!usb4_dp_port_bandwidth_mode_supported(in)) return 0; tb_port_dbg(in, "bandwidth allocation mode supported\n"); @@ -753,17 +753,17 @@ static int tb_dp_init(struct tb_tunnel *tunnel) if (ret) return ret; - return tb_dp_bw_alloc_mode_enable(tunnel); + return tb_dp_bandwidth_alloc_mode_enable(tunnel); } static void tb_dp_deinit(struct tb_tunnel *tunnel) { struct tb_port *in = tunnel->src_port; - if (!usb4_dp_port_bw_mode_supported(in)) + if (!usb4_dp_port_bandwidth_mode_supported(in)) return; - if (usb4_dp_port_bw_mode_enabled(in)) { - usb4_dp_port_set_cm_bw_mode_supported(in, false); + if (usb4_dp_port_bandwidth_mode_enabled(in)) { + usb4_dp_port_set_cm_bandwidth_mode_supported(in, false); tb_port_dbg(in, "bandwidth allocation mode disabled\n"); } } @@ -827,21 +827,22 @@ static int tb_dp_nrd_bandwidth(struct tb_tunnel *tunnel, int *max_bw) return nrd_bw; } -static int tb_dp_bw_mode_consumed_bandwidth(struct tb_tunnel *tunnel, - int *consumed_up, int *consumed_down) +static int tb_dp_bandwidth_mode_consumed_bandwidth(struct tb_tunnel *tunnel, + int *consumed_up, + int *consumed_down) { struct tb_port *out = tunnel->dst_port; struct tb_port *in = tunnel->src_port; int ret, allocated_bw, max_bw; - if (!usb4_dp_port_bw_mode_enabled(in)) + if (!usb4_dp_port_bandwidth_mode_enabled(in)) return -EOPNOTSUPP; if (!tunnel->bw_mode) return -EOPNOTSUPP; /* Read what was allocated previously if any */ - ret = usb4_dp_port_allocated_bw(in); + ret = usb4_dp_port_allocated_bandwidth(in); if (ret < 0) return ret; allocated_bw = ret; @@ -876,10 +877,10 @@ static int tb_dp_allocated_bandwidth(struct tb_tunnel *tunnel, int *allocated_up * If we have already set the allocated bandwidth then use that. * Otherwise we read it from the DPRX. */ - if (usb4_dp_port_bw_mode_enabled(in) && tunnel->bw_mode) { + if (usb4_dp_port_bandwidth_mode_enabled(in) && tunnel->bw_mode) { int ret, allocated_bw, max_bw; - ret = usb4_dp_port_allocated_bw(in); + ret = usb4_dp_port_allocated_bandwidth(in); if (ret < 0) return ret; allocated_bw = ret; @@ -911,7 +912,7 @@ static int tb_dp_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up, struct tb_port *in = tunnel->src_port; int max_bw, ret, tmp; - if (!usb4_dp_port_bw_mode_enabled(in)) + if (!usb4_dp_port_bandwidth_mode_enabled(in)) return -EOPNOTSUPP; ret = tb_dp_nrd_bandwidth(tunnel, &max_bw); @@ -920,14 +921,14 @@ static int tb_dp_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up, if (in->sw->config.depth < out->sw->config.depth) { tmp = min(*alloc_down, max_bw); - ret = usb4_dp_port_allocate_bw(in, tmp); + ret = usb4_dp_port_allocate_bandwidth(in, tmp); if (ret) return ret; *alloc_down = tmp; *alloc_up = 0; } else { tmp = min(*alloc_up, max_bw); - ret = usb4_dp_port_allocate_bw(in, tmp); + ret = usb4_dp_port_allocate_bandwidth(in, tmp); if (ret) return ret; *alloc_down = 0; @@ -1048,8 +1049,8 @@ static int tb_dp_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up, * mode is enabled first and then read the bandwidth * through those registers. */ - ret = tb_dp_bw_mode_consumed_bandwidth(tunnel, consumed_up, - consumed_down); + ret = tb_dp_bandwidth_mode_consumed_bandwidth(tunnel, consumed_up, + consumed_down); if (ret < 0) { if (ret != -EOPNOTSUPP) return ret; diff --git a/drivers/thunderbolt/usb4.c b/drivers/thunderbolt/usb4.c index fc213b038dfa..05ddb224c464 100644 --- a/drivers/thunderbolt/usb4.c +++ b/drivers/thunderbolt/usb4.c @@ -2294,13 +2294,14 @@ int usb4_dp_port_set_cm_id(struct tb_port *port, int cm_id) } /** - * usb4_dp_port_bw_mode_supported() - Is the bandwidth allocation mode supported + * usb4_dp_port_bandwidth_mode_supported() - Is the bandwidth allocation mode + * supported * @port: DP IN adapter to check * * Can be called to any DP IN adapter. Returns true if the adapter * supports USB4 bandwidth allocation mode, false otherwise. */ -bool usb4_dp_port_bw_mode_supported(struct tb_port *port) +bool usb4_dp_port_bandwidth_mode_supported(struct tb_port *port) { int ret; u32 val; @@ -2317,13 +2318,14 @@ bool usb4_dp_port_bw_mode_supported(struct tb_port *port) } /** - * usb4_dp_port_bw_mode_enabled() - Is the bandwidth allocation mode enabled + * usb4_dp_port_bandwidth_mode_enabled() - Is the bandwidth allocation mode + * enabled * @port: DP IN adapter to check * * Can be called to any DP IN adapter. Returns true if the bandwidth * allocation mode has been enabled, false otherwise. */ -bool usb4_dp_port_bw_mode_enabled(struct tb_port *port) +bool usb4_dp_port_bandwidth_mode_enabled(struct tb_port *port) { int ret; u32 val; @@ -2340,7 +2342,8 @@ bool usb4_dp_port_bw_mode_enabled(struct tb_port *port) } /** - * usb4_dp_port_set_cm_bw_mode_supported() - Set/clear CM support for bandwidth allocation mode + * usb4_dp_port_set_cm_bandwidth_mode_supported() - Set/clear CM support for + * bandwidth allocation mode * @port: DP IN adapter * @supported: Does the CM support bandwidth allocation mode * @@ -2349,7 +2352,8 @@ bool usb4_dp_port_bw_mode_enabled(struct tb_port *port) * otherwise. Specifically returns %-OPNOTSUPP if the passed in adapter * does not support this. */ -int usb4_dp_port_set_cm_bw_mode_supported(struct tb_port *port, bool supported) +int usb4_dp_port_set_cm_bandwidth_mode_supported(struct tb_port *port, + bool supported) { u32 val; int ret; @@ -2623,7 +2627,7 @@ int usb4_dp_port_set_granularity(struct tb_port *port, int granularity) } /** - * usb4_dp_port_set_estimated_bw() - Set estimated bandwidth + * usb4_dp_port_set_estimated_bandwidth() - Set estimated bandwidth * @port: DP IN adapter * @bw: Estimated bandwidth in Mb/s. * @@ -2633,7 +2637,7 @@ int usb4_dp_port_set_granularity(struct tb_port *port, int granularity) * and negative errno otherwise. Specifically returns %-EOPNOTSUPP if * the adapter does not support this. */ -int usb4_dp_port_set_estimated_bw(struct tb_port *port, int bw) +int usb4_dp_port_set_estimated_bandwidth(struct tb_port *port, int bw) { u32 val, granularity; int ret; @@ -2659,14 +2663,14 @@ int usb4_dp_port_set_estimated_bw(struct tb_port *port, int bw) } /** - * usb4_dp_port_allocated_bw() - Return allocated bandwidth + * usb4_dp_port_allocated_bandwidth() - Return allocated bandwidth * @port: DP IN adapter * * Reads and returns allocated bandwidth for @port in Mb/s (taking into * account the programmed granularity). Returns negative errno in case * of error. */ -int usb4_dp_port_allocated_bw(struct tb_port *port) +int usb4_dp_port_allocated_bandwidth(struct tb_port *port) { u32 val, granularity; int ret; @@ -2752,7 +2756,7 @@ static int usb4_dp_port_wait_and_clear_cm_ack(struct tb_port *port, } /** - * usb4_dp_port_allocate_bw() - Set allocated bandwidth + * usb4_dp_port_allocate_bandwidth() - Set allocated bandwidth * @port: DP IN adapter * @bw: New allocated bandwidth in Mb/s * @@ -2760,7 +2764,7 @@ static int usb4_dp_port_wait_and_clear_cm_ack(struct tb_port *port, * driver). Takes into account the programmed granularity. Returns %0 in * success and negative errno in case of error. */ -int usb4_dp_port_allocate_bw(struct tb_port *port, int bw) +int usb4_dp_port_allocate_bandwidth(struct tb_port *port, int bw) { u32 val, granularity; int ret; @@ -2794,7 +2798,7 @@ int usb4_dp_port_allocate_bw(struct tb_port *port, int bw) } /** - * usb4_dp_port_requested_bw() - Read requested bandwidth + * usb4_dp_port_requested_bandwidth() - Read requested bandwidth * @port: DP IN adapter * * Reads the DPCD (graphics driver) requested bandwidth and returns it @@ -2803,7 +2807,7 @@ int usb4_dp_port_allocate_bw(struct tb_port *port, int bw) * the adapter does not support bandwidth allocation mode, and %ENODATA * if there is no active bandwidth request from the graphics driver. */ -int usb4_dp_port_requested_bw(struct tb_port *port) +int usb4_dp_port_requested_bandwidth(struct tb_port *port) { u32 val, granularity; int ret; -- cgit v1.2.3 From 2d7e047297983dd29c29c41b8b7034d64f861aa2 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 23 Jan 2023 10:42:58 +0200 Subject: thunderbolt: Add DisplayPort 2.x tunneling support This adds support for the UHBR (Ultra High Bit Rate) bandwidths introduced with DisplayPort 2.0 (and refined in 2.1). These can go up to 80 Gbit/s and their support is represent in additional bits in the DP IN capability. This updates the DisplayPort tunneling to support these new rates too. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/tb_regs.h | 3 ++ drivers/thunderbolt/tunnel.c | 100 +++++++++++++++++++++++++++++++++++------- 2 files changed, 87 insertions(+), 16 deletions(-) diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index c95fc7fe7adf..cf9f2370878a 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -450,6 +450,9 @@ struct tb_regs_port_header { #define DP_COMMON_CAP_1_LANE 0x0 #define DP_COMMON_CAP_2_LANES 0x1 #define DP_COMMON_CAP_4_LANES 0x2 +#define DP_COMMON_CAP_UHBR10 BIT(17) +#define DP_COMMON_CAP_UHBR20 BIT(18) +#define DP_COMMON_CAP_UHBR13_5 BIT(19) #define DP_COMMON_CAP_LTTPR_NS BIT(27) #define DP_COMMON_CAP_BW_MODE BIT(28) #define DP_COMMON_CAP_DPRX_DONE BIT(31) diff --git a/drivers/thunderbolt/tunnel.c b/drivers/thunderbolt/tunnel.c index 224f4265b755..a6810fb36860 100644 --- a/drivers/thunderbolt/tunnel.c +++ b/drivers/thunderbolt/tunnel.c @@ -418,6 +418,10 @@ static int tb_dp_cm_handshake(struct tb_port *in, struct tb_port *out, return -ETIMEDOUT; } +/* + * Returns maximum possible rate from capability supporting only DP 2.0 + * and below. Used when DP BW allocation mode is not enabled. + */ static inline u32 tb_dp_cap_get_rate(u32 val) { u32 rate = (val & DP_COMMON_CAP_RATE_MASK) >> DP_COMMON_CAP_RATE_SHIFT; @@ -436,6 +440,28 @@ static inline u32 tb_dp_cap_get_rate(u32 val) } } +/* + * Returns maximum possible rate from capability supporting DP 2.1 + * UHBR20, 13.5 and 10 rates as well. Use only when DP BW allocation + * mode is enabled. + */ +static inline u32 tb_dp_cap_get_rate_ext(u32 val) +{ + if (val & DP_COMMON_CAP_UHBR20) + return 20000; + else if (val & DP_COMMON_CAP_UHBR13_5) + return 13500; + else if (val & DP_COMMON_CAP_UHBR10) + return 10000; + + return tb_dp_cap_get_rate(val); +} + +static inline bool tb_dp_is_uhbr_rate(unsigned int rate) +{ + return rate >= 10000; +} + static inline u32 tb_dp_cap_set_rate(u32 val, u32 rate) { val &= ~DP_COMMON_CAP_RATE_MASK; @@ -498,7 +524,9 @@ static inline u32 tb_dp_cap_set_lanes(u32 val, u32 lanes) static unsigned int tb_dp_bandwidth(unsigned int rate, unsigned int lanes) { - /* Tunneling removes the DP 8b/10b encoding */ + /* Tunneling removes the DP 8b/10b 128/132b encoding */ + if (tb_dp_is_uhbr_rate(rate)) + return rate * lanes * 128 / 132; return rate * lanes * 8 / 10; } @@ -691,6 +719,19 @@ static int tb_dp_bandwidth_alloc_mode_enable(struct tb_tunnel *tunnel) if (ret) return ret; + /* + * Pick up granularity that supports maximum possible bandwidth. + * For that we use the UHBR rates too. + */ + in_rate = tb_dp_cap_get_rate_ext(in_dp_cap); + out_rate = tb_dp_cap_get_rate_ext(out_dp_cap); + rate = min(in_rate, out_rate); + tmp = tb_dp_bandwidth(rate, lanes); + + tb_port_dbg(in, + "maximum bandwidth through allocation mode %u Mb/s x%u = %u Mb/s\n", + rate, lanes, tmp); + for (granularity = 250; tmp / granularity > 255 && granularity <= 1000; granularity *= 2) ; @@ -806,15 +847,42 @@ static int tb_dp_activate(struct tb_tunnel *tunnel, bool active) } /* max_bw is rounded up to next granularity */ -static int tb_dp_nrd_bandwidth(struct tb_tunnel *tunnel, int *max_bw) +static int tb_dp_bandwidth_mode_maximum_bandwidth(struct tb_tunnel *tunnel, + int *max_bw) { struct tb_port *in = tunnel->src_port; int ret, rate, lanes, nrd_bw; + u32 cap; - ret = usb4_dp_port_nrd(in, &rate, &lanes); + /* + * DP IN adapter DP_LOCAL_CAP gets updated to the lowest AUX + * read parameter values so this so we can use this to determine + * the maximum possible bandwidth over this link. + * + * See USB4 v2 spec 1.0 10.4.4.5. + */ + ret = tb_port_read(in, &cap, TB_CFG_PORT, + in->cap_adap + DP_LOCAL_CAP, 1); if (ret) return ret; + rate = tb_dp_cap_get_rate_ext(cap); + if (tb_dp_is_uhbr_rate(rate)) { + /* + * When UHBR is used there is no reduction in lanes so + * we can use this directly. + */ + lanes = tb_dp_cap_get_lanes(cap); + } else { + /* + * If there is no UHBR supported then check the + * non-reduced rate and lanes. + */ + ret = usb4_dp_port_nrd(in, &rate, &lanes); + if (ret) + return ret; + } + nrd_bw = tb_dp_bandwidth(rate, lanes); if (max_bw) { @@ -847,7 +915,7 @@ static int tb_dp_bandwidth_mode_consumed_bandwidth(struct tb_tunnel *tunnel, return ret; allocated_bw = ret; - ret = tb_dp_nrd_bandwidth(tunnel, &max_bw); + ret = tb_dp_bandwidth_mode_maximum_bandwidth(tunnel, &max_bw); if (ret < 0) return ret; if (allocated_bw == max_bw) @@ -885,7 +953,7 @@ static int tb_dp_allocated_bandwidth(struct tb_tunnel *tunnel, int *allocated_up return ret; allocated_bw = ret; - ret = tb_dp_nrd_bandwidth(tunnel, &max_bw); + ret = tb_dp_bandwidth_mode_maximum_bandwidth(tunnel, &max_bw); if (ret < 0) return ret; if (allocated_bw == max_bw) @@ -915,7 +983,7 @@ static int tb_dp_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up, if (!usb4_dp_port_bandwidth_mode_enabled(in)) return -EOPNOTSUPP; - ret = tb_dp_nrd_bandwidth(tunnel, &max_bw); + ret = tb_dp_bandwidth_mode_maximum_bandwidth(tunnel, &max_bw); if (ret < 0) return ret; @@ -938,6 +1006,9 @@ static int tb_dp_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up, /* Now we can use BW mode registers to figure out the bandwidth */ /* TODO: need to handle discovery too */ tunnel->bw_mode = true; + + tb_port_dbg(in, "allocated bandwidth through allocation mode %d Mb/s\n", + tmp); return 0; } @@ -1012,23 +1083,20 @@ static int tb_dp_maximum_bandwidth(struct tb_tunnel *tunnel, int *max_up, int *max_down) { struct tb_port *in = tunnel->src_port; - u32 rate, lanes; int ret; - /* - * DP IN adapter DP_LOCAL_CAP gets updated to the lowest AUX read - * parameter values so this so we can use this to determine the - * maximum possible bandwidth over this link. - */ - ret = tb_dp_read_cap(tunnel, DP_LOCAL_CAP, &rate, &lanes); - if (ret) + if (!usb4_dp_port_bandwidth_mode_enabled(in)) + return -EOPNOTSUPP; + + ret = tb_dp_bandwidth_mode_maximum_bandwidth(tunnel, NULL); + if (ret < 0) return ret; if (in->sw->config.depth < tunnel->dst_port->sw->config.depth) { *max_up = 0; - *max_down = tb_dp_bandwidth(rate, lanes); + *max_down = ret; } else { - *max_up = tb_dp_bandwidth(rate, lanes); + *max_up = ret; *max_down = 0; } -- cgit v1.2.3 From 481012b479fe6d8dd4e01d739c359a8d99d074a9 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Mon, 14 Feb 2022 14:09:50 +0200 Subject: thunderbolt: Add test case for 3 DisplayPort tunnels Intel Barlow Ridge Thunderbolt controller has 3 DP IN adapters. This allows 3 simultaneus DisplayPort tunnels through either one or two USB4 downstream ports (in any possible configuration). Add test case for this. Signed-off-by: Mika Westerberg --- drivers/thunderbolt/test.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/drivers/thunderbolt/test.c b/drivers/thunderbolt/test.c index 24c06e7354cd..9475c6698c7d 100644 --- a/drivers/thunderbolt/test.c +++ b/drivers/thunderbolt/test.c @@ -170,6 +170,23 @@ static struct tb_switch *alloc_host_usb4(struct kunit *test) return sw; } +static struct tb_switch *alloc_host_br(struct kunit *test) +{ + struct tb_switch *sw; + + sw = alloc_host_usb4(test); + if (!sw) + return NULL; + + sw->ports[10].config.type = TB_TYPE_DP_HDMI_IN; + sw->ports[10].config.max_in_hop_id = 9; + sw->ports[10].config.max_out_hop_id = 9; + sw->ports[10].cap_adap = -1; + sw->ports[10].disabled = false; + + return sw; +} + static struct tb_switch *alloc_dev_default(struct kunit *test, struct tb_switch *parent, u64 route, bool bonded) @@ -1583,6 +1600,71 @@ static void tb_test_tunnel_dp_max_length(struct kunit *test) tb_tunnel_free(tunnel); } +static void tb_test_tunnel_3dp(struct kunit *test) +{ + struct tb_switch *host, *dev1, *dev2, *dev3, *dev4, *dev5; + struct tb_port *in1, *in2, *in3, *out1, *out2, *out3; + struct tb_tunnel *tunnel1, *tunnel2, *tunnel3; + + /* + * Create 3 DP tunnels from Host to Devices #2, #5 and #4. + * + * [Host] + * 3 | + * 1 | + * [Device #1] + * 3 / | 5 \ 7 + * 1 / | \ 1 + * [Device #2] | [Device #4] + * | 1 + * [Device #3] + * | 5 + * | 1 + * [Device #5] + */ + host = alloc_host_br(test); + dev1 = alloc_dev_default(test, host, 0x3, true); + dev2 = alloc_dev_default(test, dev1, 0x303, true); + dev3 = alloc_dev_default(test, dev1, 0x503, true); + dev4 = alloc_dev_default(test, dev1, 0x703, true); + dev5 = alloc_dev_default(test, dev3, 0x50503, true); + + in1 = &host->ports[5]; + in2 = &host->ports[6]; + in3 = &host->ports[10]; + + out1 = &dev2->ports[13]; + out2 = &dev5->ports[13]; + out3 = &dev4->ports[14]; + + tunnel1 = tb_tunnel_alloc_dp(NULL, in1, out1, 1, 0, 0); + KUNIT_ASSERT_TRUE(test, tunnel1 != NULL); + KUNIT_EXPECT_EQ(test, tunnel1->type, TB_TUNNEL_DP); + KUNIT_EXPECT_PTR_EQ(test, tunnel1->src_port, in1); + KUNIT_EXPECT_PTR_EQ(test, tunnel1->dst_port, out1); + KUNIT_ASSERT_EQ(test, tunnel1->npaths, 3); + KUNIT_ASSERT_EQ(test, tunnel1->paths[0]->path_length, 3); + + tunnel2 = tb_tunnel_alloc_dp(NULL, in2, out2, 1, 0, 0); + KUNIT_ASSERT_TRUE(test, tunnel2 != NULL); + KUNIT_EXPECT_EQ(test, tunnel2->type, TB_TUNNEL_DP); + KUNIT_EXPECT_PTR_EQ(test, tunnel2->src_port, in2); + KUNIT_EXPECT_PTR_EQ(test, tunnel2->dst_port, out2); + KUNIT_ASSERT_EQ(test, tunnel2->npaths, 3); + KUNIT_ASSERT_EQ(test, tunnel2->paths[0]->path_length, 4); + + tunnel3 = tb_tunnel_alloc_dp(NULL, in3, out3, 1, 0, 0); + KUNIT_ASSERT_TRUE(test, tunnel3 != NULL); + KUNIT_EXPECT_EQ(test, tunnel3->type, TB_TUNNEL_DP); + KUNIT_EXPECT_PTR_EQ(test, tunnel3->src_port, in3); + KUNIT_EXPECT_PTR_EQ(test, tunnel3->dst_port, out3); + KUNIT_ASSERT_EQ(test, tunnel3->npaths, 3); + KUNIT_ASSERT_EQ(test, tunnel3->paths[0]->path_length, 3); + + tb_tunnel_free(tunnel2); + tb_tunnel_free(tunnel1); +} + static void tb_test_tunnel_usb3(struct kunit *test) { struct tb_switch *host, *dev1, *dev2; @@ -2790,6 +2872,7 @@ static struct kunit_case tb_test_cases[] = { KUNIT_CASE(tb_test_tunnel_dp_chain), KUNIT_CASE(tb_test_tunnel_dp_tree), KUNIT_CASE(tb_test_tunnel_dp_max_length), + KUNIT_CASE(tb_test_tunnel_3dp), KUNIT_CASE(tb_test_tunnel_port_on_path), KUNIT_CASE(tb_test_tunnel_usb3), KUNIT_CASE(tb_test_tunnel_dma), -- cgit v1.2.3 From 1e35f074399dece73d5df11847d4a0d7a6f49434 Mon Sep 17 00:00:00 2001 From: Frank Wang Date: Fri, 16 Jun 2023 15:52:40 +0800 Subject: usb: typec: tcpm: fix cc role at port reset In the current implementation, the tcpm set CC1/CC2 role to open when it do port reset would cause the VBUS removed by the Type-C partner. This sets CC1/CC2 according to the default state of port to fix it. Signed-off-by: Frank Wang Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20230616075241.27690-1-frank.wang@rock-chips.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 3c6b0c8e2d3a..9f6aaa3e70ca 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -4885,7 +4885,8 @@ static void run_state_machine(struct tcpm_port *port) break; case PORT_RESET: tcpm_reset_port(port); - tcpm_set_cc(port, TYPEC_CC_OPEN); + tcpm_set_cc(port, tcpm_default_state(port) == SNK_UNATTACHED ? + TYPEC_CC_RD : tcpm_rp_cc(port)); tcpm_set_state(port, PORT_RESET_WAIT_OFF, PD_T_ERROR_RECOVERY); break; -- cgit v1.2.3 From 8be558dcffe69b078b34b1fa93b82acaf4ce4957 Mon Sep 17 00:00:00 2001 From: Frank Wang Date: Fri, 16 Jun 2023 15:52:41 +0800 Subject: usb: typec: tcpm: add get max power support Traverse fixed pdos to calculate the maximum power that the charger can provide, and it can be get by POWER_SUPPLY_PROP_INPUT_POWER_LIMIT property. Signed-off-by: Frank Wang Reviewed-by: Guenter Roeck Link: https://lore.kernel.org/r/20230616075241.27690-2-frank.wang@rock-chips.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/tcpm/tcpm.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 9f6aaa3e70ca..829d75ebab42 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -6340,6 +6340,27 @@ static int tcpm_psy_get_current_now(struct tcpm_port *port, return 0; } +static int tcpm_psy_get_input_power_limit(struct tcpm_port *port, + union power_supply_propval *val) +{ + unsigned int src_mv, src_ma, max_src_uw = 0; + unsigned int i, tmp; + + for (i = 0; i < port->nr_source_caps; i++) { + u32 pdo = port->source_caps[i]; + + if (pdo_type(pdo) == PDO_TYPE_FIXED) { + src_mv = pdo_fixed_voltage(pdo); + src_ma = pdo_max_current(pdo); + tmp = src_mv * src_ma; + max_src_uw = tmp > max_src_uw ? tmp : max_src_uw; + } + } + + val->intval = max_src_uw; + return 0; +} + static int tcpm_psy_get_prop(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -6369,6 +6390,9 @@ static int tcpm_psy_get_prop(struct power_supply *psy, case POWER_SUPPLY_PROP_CURRENT_NOW: ret = tcpm_psy_get_current_now(port, val); break; + case POWER_SUPPLY_PROP_INPUT_POWER_LIMIT: + tcpm_psy_get_input_power_limit(port, val); + break; default: ret = -EINVAL; break; -- cgit v1.2.3 From ee400a1be11527e1ea58b716b338b9fb6665b8d6 Mon Sep 17 00:00:00 2001 From: Azeem Shaikh Date: Thu, 15 Jun 2023 18:03:18 +0000 Subject: usb: gadget: function: printer: Replace strlcpy with strscpy strlcpy() reads the entire source buffer first. This read may exceed the destination size limit. This is both inefficient and can lead to linear read overflows if a source string is not NUL-terminated [1]. In an effort to remove strlcpy() completely [2], replace strlcpy() here with strscpy(). Direct replacement is safe here since return value of -errno is used to check for truncation instead of PAGE_SIZE. [1] https://www.kernel.org/doc/html/latest/process/deprecated.html#strlcpy [2] https://github.com/KSPP/linux/issues/89 Signed-off-by: Azeem Shaikh Link: https://lore.kernel.org/r/20230615180318.400639-1-azeemshaikh38@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_printer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index 28db3e336e7d..b91425611969 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -1211,8 +1211,8 @@ static ssize_t f_printer_opts_pnp_string_show(struct config_item *item, if (!opts->pnp_string) goto unlock; - result = strlcpy(page, opts->pnp_string, PAGE_SIZE); - if (result >= PAGE_SIZE) { + result = strscpy(page, opts->pnp_string, PAGE_SIZE); + if (result < 1) { result = PAGE_SIZE; } else if (page[result - 1] != '\n' && result + 1 < PAGE_SIZE) { page[result++] = '\n'; -- cgit v1.2.3 From 55f90c3f83c5d26ce6b1a6a01a4f435860376be4 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 15 Jun 2023 15:40:52 +0100 Subject: usb: cdns2: Fix spelling mistake in a trace message "Wakupe" -> "Wakeup" There is a spelling mistake in a trace message. Fix it. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20230615144052.2254528-1-colin.i.king@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/cdns2/cdns2-debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/cdns2/cdns2-debug.h b/drivers/usb/gadget/udc/cdns2/cdns2-debug.h index fd22ae949008..be9ae0d28a40 100644 --- a/drivers/usb/gadget/udc/cdns2/cdns2-debug.h +++ b/drivers/usb/gadget/udc/cdns2/cdns2-debug.h @@ -36,7 +36,7 @@ static inline const char *cdns2_decode_usb_irq(char *str, size_t size, ret += snprintf(str + ret, size - ret, ", EXT: 0x%02x - ", ext_irq); if (ext_irq & EXTIRQ_WAKEUP) - ret += snprintf(str + ret, size - ret, "Wakupe "); + ret += snprintf(str + ret, size - ret, "Wakeup "); if (ext_irq & EXTIRQ_VBUSFAULT_FALL) ret += snprintf(str + ret, size - ret, "VBUS_FALL "); if (ext_irq & EXTIRQ_VBUSFAULT_RISE) -- cgit v1.2.3 From 6059d8124388c123f61a1affd1ebe04083dac588 Mon Sep 17 00:00:00 2001 From: Varadarajan Narayanan Date: Fri, 9 Jun 2023 11:26:30 +0530 Subject: dt-bindings: usb: dwc3: Add IPQ9574 compatible * Document the IPQ9574 dwc3 compatible. * Make power-domains as optional since IPQ9574 doesn't have GDSCs Reviewed-by: Rob Herring Signed-off-by: Varadarajan Narayanan Link: https://lore.kernel.org/r/27fc578e549e12a4d689cdd434107964b529c4f4.1686289721.git.quic_varada@quicinc.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/qcom,dwc3.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml index d84281926f10..5c1322972f4e 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml @@ -17,6 +17,7 @@ properties: - qcom,ipq6018-dwc3 - qcom,ipq8064-dwc3 - qcom,ipq8074-dwc3 + - qcom,ipq9574-dwc3 - qcom,msm8953-dwc3 - qcom,msm8994-dwc3 - qcom,msm8996-dwc3 @@ -133,7 +134,6 @@ required: - "#address-cells" - "#size-cells" - ranges - - power-domains - clocks - clock-names - interrupts @@ -177,6 +177,7 @@ allOf: compatible: contains: enum: + - qcom,ipq9574-dwc3 - qcom,msm8953-dwc3 - qcom,msm8996-dwc3 - qcom,msm8998-dwc3 -- cgit v1.2.3 From 5aa735a4742c5ffff2fe34f764cbcbd917e100ae Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 18 May 2023 19:27:48 +0800 Subject: dt-bindings: usb: Add StarFive JH7110 USB controller StarFive JH7110 platforms USB have a wrapper module around the Cadence USBSS-DRD controller. Add binding information doc for that. Signed-off-by: Minda Chen Reviewed-by: Peter Chen Reviewed-by: Hal Feng Reviewed-by: Conor Dooley Link: https://lore.kernel.org/r/20230518112750.57924-6-minda.chen@starfivetech.com Signed-off-by: Greg Kroah-Hartman --- .../bindings/usb/starfive,jh7110-usb.yaml | 115 +++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 Documentation/devicetree/bindings/usb/starfive,jh7110-usb.yaml diff --git a/Documentation/devicetree/bindings/usb/starfive,jh7110-usb.yaml b/Documentation/devicetree/bindings/usb/starfive,jh7110-usb.yaml new file mode 100644 index 000000000000..24aa9c10d6ab --- /dev/null +++ b/Documentation/devicetree/bindings/usb/starfive,jh7110-usb.yaml @@ -0,0 +1,115 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/starfive,jh7110-usb.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 wrapper module for the Cadence USBSS-DRD controller + +maintainers: + - Minda Chen + +properties: + compatible: + const: starfive,jh7110-usb + + ranges: true + + starfive,stg-syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + - items: + - description: phandle to System Register Controller stg_syscon node. + - description: dr mode register offset of STG_SYSCONSAIF__SYSCFG register for USB. + description: + The phandle to System Register Controller syscon node and the offset + of STG_SYSCONSAIF__SYSCFG register for USB. + + dr_mode: + enum: [host, otg, peripheral] + + "#address-cells": + enum: [1, 2] + + "#size-cells": + enum: [1, 2] + + clocks: + items: + - description: link power management clock + - description: standby clock + - description: APB clock + - description: AXI clock + - description: UTMI APB clock + + clock-names: + items: + - const: lpm + - const: stb + - const: apb + - const: axi + - const: utmi_apb + + resets: + items: + - description: Power up reset + - description: APB clock reset + - description: AXI clock reset + - description: UTMI APB clock reset + + reset-names: + items: + - const: pwrup + - const: apb + - const: axi + - const: utmi_apb + +patternProperties: + "^usb@[0-9a-f]+$": + $ref: cdns,usb3.yaml# + description: Required child node + +required: + - compatible + - ranges + - starfive,stg-syscon + - '#address-cells' + - '#size-cells' + - dr_mode + - clocks + - resets + +additionalProperties: false + +examples: + - | + usb@10100000 { + compatible = "starfive,jh7110-usb"; + ranges = <0x0 0x10100000 0x100000>; + #address-cells = <1>; + #size-cells = <1>; + starfive,stg-syscon = <&stg_syscon 0x4>; + clocks = <&syscrg 4>, + <&stgcrg 5>, + <&stgcrg 1>, + <&stgcrg 3>, + <&stgcrg 2>; + clock-names = "lpm", "stb", "apb", "axi", "utmi_apb"; + resets = <&stgcrg 10>, + <&stgcrg 8>, + <&stgcrg 7>, + <&stgcrg 9>; + reset-names = "pwrup", "apb", "axi", "utmi_apb"; + dr_mode = "host"; + + usb@0 { + compatible = "cdns,usb3"; + reg = <0x0 0x10000>, + <0x10000 0x10000>, + <0x20000 0x10000>; + reg-names = "otg", "xhci", "dev"; + interrupts = <100>, <108>, <110>; + interrupt-names = "host", "peripheral", "otg"; + maximum-speed = "super-speed"; + }; + }; -- cgit v1.2.3 From 34d401a1910dde53356fa5ac03649085fd20e5e3 Mon Sep 17 00:00:00 2001 From: Piyush Mehta Date: Mon, 19 Jun 2023 16:20:32 +0530 Subject: dt-bindings: usb: dwc3: Add interrupt-names property support for wakeup interrupt The hibernation feature enabled for Xilinx Versal NET SoC in DWC3 IP. As the DWC3 IP supports the hibernation feature, to handle the wakeup or hibernation interrupt, add host mode "wakeup" interrupt-names optional property in the binding schema to capture remote-wakeup and connect/ disconnect event in the hibernation state and increased maxItems to 4 for the interrupts and interrupt-names property. We have a dedicated IRQ line specifically for the hibernation feature. When the "wakeup" IRQ line is triggered, it initiates a hibernation interrupt, causing the system to wake up from the hibernation state. Signed-off-by: Piyush Mehta Acked-by: Conor Dooley Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20230619105032.2888128-1-piyush.mehta@amd.com Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/usb/snps,dwc3.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml index 4f7625955ccc..a696f23730d3 100644 --- a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml @@ -44,15 +44,15 @@ properties: It's either a single common DWC3 interrupt (dwc_usb3) or individual interrupts for the host, gadget and DRD modes. minItems: 1 - maxItems: 3 + maxItems: 4 interrupt-names: minItems: 1 - maxItems: 3 + maxItems: 4 oneOf: - const: dwc_usb3 - items: - enum: [host, peripheral, otg] + enum: [host, peripheral, otg, wakeup] clocks: description: -- cgit v1.2.3 From 044a61158b9e00ee4b69bc4fa0f3e720b5dd669a Mon Sep 17 00:00:00 2001 From: Ivan Orlov Date: Tue, 20 Jun 2023 11:44:13 +0200 Subject: USB: roles: make role_class a static const structure Now that the driver core allows for struct class to be in read-only memory, move the role_class structure to be declared at build time placing it into read-only memory, instead of having to be dynamically allocated at load time. Suggested-by: Greg Kroah-Hartman Signed-off-by: Ivan Orlov Link: https://lore.kernel.org/r/20230620094412.508580-7-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/roles/class.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/usb/roles/class.c b/drivers/usb/roles/class.c index 0395bd5dbd3e..ae41578bd014 100644 --- a/drivers/usb/roles/class.c +++ b/drivers/usb/roles/class.c @@ -14,7 +14,9 @@ #include #include -static struct class *role_class; +static const struct class role_class = { + .name = "usb_role", +}; struct usb_role_switch { struct device dev; @@ -95,7 +97,7 @@ static void *usb_role_switch_match(const struct fwnode_handle *fwnode, const cha if (id && !fwnode_property_present(fwnode, id)) return NULL; - dev = class_find_device_by_fwnode(role_class, fwnode); + dev = class_find_device_by_fwnode(&role_class, fwnode); return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER); } @@ -111,7 +113,7 @@ usb_role_switch_is_parent(struct fwnode_handle *fwnode) return NULL; } - dev = class_find_device_by_fwnode(role_class, parent); + dev = class_find_device_by_fwnode(&role_class, parent); fwnode_handle_put(parent); return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER); } @@ -191,7 +193,7 @@ usb_role_switch_find_by_fwnode(const struct fwnode_handle *fwnode) if (!fwnode) return NULL; - dev = class_find_device_by_fwnode(role_class, fwnode); + dev = class_find_device_by_fwnode(&role_class, fwnode); if (dev) WARN_ON(!try_module_get(dev->parent->driver->owner)); @@ -338,7 +340,7 @@ usb_role_switch_register(struct device *parent, sw->dev.parent = parent; sw->dev.fwnode = desc->fwnode; - sw->dev.class = role_class; + sw->dev.class = &role_class; sw->dev.type = &usb_role_dev_type; dev_set_drvdata(&sw->dev, desc->driver_data); dev_set_name(&sw->dev, "%s-role-switch", @@ -392,14 +394,13 @@ EXPORT_SYMBOL_GPL(usb_role_switch_get_drvdata); static int __init usb_roles_init(void) { - role_class = class_create("usb_role"); - return PTR_ERR_OR_ZERO(role_class); + return class_register(&role_class); } subsys_initcall(usb_roles_init); static void __exit usb_roles_exit(void) { - class_destroy(role_class); + class_unregister(&role_class); } module_exit(usb_roles_exit); -- cgit v1.2.3 From 8e99143649ad4838d58aeac9da43a5726b841366 Mon Sep 17 00:00:00 2001 From: Ivan Orlov Date: Tue, 20 Jun 2023 11:44:14 +0200 Subject: USB: gadget: udc: core: make udc_class a static const structure Now that the driver core allows for struct class to be in read-only memory, move the udc_class structure to be declared at build time placing it into read-only memory, instead of having to be dynamically allocated at load time. Suggested-by: Greg Kroah-Hartman Signed-off-by: Ivan Orlov Link: https://lore.kernel.org/r/20230620094412.508580-8-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/udc/core.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 52e6d2e84e35..b2bb6336902f 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -50,7 +50,7 @@ struct usb_udc { bool started; }; -static struct class *udc_class; +static const struct class udc_class; static LIST_HEAD(udc_list); /* Protects udc_list, udc->driver, driver->is_bound, and related calls */ @@ -1312,7 +1312,7 @@ int usb_add_gadget(struct usb_gadget *gadget) device_initialize(&udc->dev); udc->dev.release = usb_udc_release; - udc->dev.class = udc_class; + udc->dev.class = &udc_class; udc->dev.groups = usb_udc_attr_groups; udc->dev.parent = gadget->dev.parent; ret = dev_set_name(&udc->dev, "%s", @@ -1774,6 +1774,11 @@ static int usb_udc_uevent(const struct device *dev, struct kobj_uevent_env *env) return 0; } +static const struct class udc_class = { + .name = "udc", + .dev_uevent = usb_udc_uevent, +}; + static const struct bus_type gadget_bus_type = { .name = "gadget", .probe = gadget_bind_driver, @@ -1785,18 +1790,13 @@ static int __init usb_udc_init(void) { int rc; - udc_class = class_create("udc"); - if (IS_ERR(udc_class)) { - pr_err("failed to create udc class --> %ld\n", - PTR_ERR(udc_class)); - return PTR_ERR(udc_class); - } - - udc_class->dev_uevent = usb_udc_uevent; + rc = class_register(&udc_class); + if (rc) + return rc; rc = bus_register(&gadget_bus_type); if (rc) - class_destroy(udc_class); + class_unregister(&udc_class); return rc; } subsys_initcall(usb_udc_init); @@ -1804,7 +1804,7 @@ subsys_initcall(usb_udc_init); static void __exit usb_udc_exit(void) { bus_unregister(&gadget_bus_type); - class_destroy(udc_class); + class_unregister(&udc_class); } module_exit(usb_udc_exit); -- cgit v1.2.3 From e571e843f0ce005503471707fa02e892ba2a6f35 Mon Sep 17 00:00:00 2001 From: Ivan Orlov Date: Tue, 20 Jun 2023 11:44:15 +0200 Subject: USB: mon: make mon_bin_class a static const structure Now that the driver core allows for struct class to be in read-only memory, move the mon_bin_class structure to be declared at build time placing it into read-only memory, instead of having to be dynamically allocated at load time. Suggested-by: Greg Kroah-Hartman Signed-off-by: Ivan Orlov Link: https://lore.kernel.org/r/20230620094412.508580-9-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/mon/mon_bin.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index 952c56789258..9ca9305243fe 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -213,7 +213,10 @@ static unsigned char xfer_to_pipe[4] = { PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT }; -static struct class *mon_bin_class; +static const struct class mon_bin_class = { + .name = "usbmon", +}; + static dev_t mon_bin_dev0; static struct cdev mon_bin_cdev; @@ -1360,7 +1363,7 @@ int mon_bin_add(struct mon_bus *mbus, const struct usb_bus *ubus) if (minor >= MON_BIN_MAX_MINOR) return 0; - dev = device_create(mon_bin_class, ubus ? ubus->controller : NULL, + dev = device_create(&mon_bin_class, ubus ? ubus->controller : NULL, MKDEV(MAJOR(mon_bin_dev0), minor), NULL, "usbmon%d", minor); if (IS_ERR(dev)) @@ -1372,18 +1375,16 @@ int mon_bin_add(struct mon_bus *mbus, const struct usb_bus *ubus) void mon_bin_del(struct mon_bus *mbus) { - device_destroy(mon_bin_class, mbus->classdev->devt); + device_destroy(&mon_bin_class, mbus->classdev->devt); } int __init mon_bin_init(void) { int rc; - mon_bin_class = class_create("usbmon"); - if (IS_ERR(mon_bin_class)) { - rc = PTR_ERR(mon_bin_class); + rc = class_register(&mon_bin_class); + if (rc) goto err_class; - } rc = alloc_chrdev_region(&mon_bin_dev0, 0, MON_BIN_MAX_MINOR, "usbmon"); if (rc < 0) @@ -1401,7 +1402,7 @@ int __init mon_bin_init(void) err_add: unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR); err_dev: - class_destroy(mon_bin_class); + class_unregister(&mon_bin_class); err_class: return rc; } @@ -1410,5 +1411,5 @@ void mon_bin_exit(void) { cdev_del(&mon_bin_cdev); unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR); - class_destroy(mon_bin_class); + class_unregister(&mon_bin_class); } -- cgit v1.2.3 From 2c10e7a049dad73503da41c7754add91cb7b35d9 Mon Sep 17 00:00:00 2001 From: Ivan Orlov Date: Tue, 20 Jun 2023 11:44:16 +0200 Subject: USB: gadget: f_printer: make usb_gadget_class a static const structure Now that the driver core allows for struct class to be in read-only memory, move the usb_gadget_class structure to be declared at build time placing it into read-only memory, instead of having to be dynamically allocated at load time. Suggested-by: Greg Kroah-Hartman Signed-off-by: Ivan Orlov Link: https://lore.kernel.org/r/20230620094412.508580-10-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_printer.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index b91425611969..076dd4c1be96 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -54,7 +54,10 @@ #define DEFAULT_Q_LEN 10 /* same as legacy g_printer gadget */ static int major, minors; -static struct class *usb_gadget_class; +static const struct class usb_gadget_class = { + .name = "usb_printer_gadget", +}; + static DEFINE_IDA(printer_ida); static DEFINE_MUTEX(printer_ida_lock); /* protects access do printer_ida */ @@ -1120,7 +1123,7 @@ autoconf_fail: /* Setup the sysfs files for the printer gadget. */ devt = MKDEV(major, dev->minor); - pdev = device_create(usb_gadget_class, NULL, devt, + pdev = device_create(&usb_gadget_class, NULL, devt, NULL, "g_printer%d", dev->minor); if (IS_ERR(pdev)) { ERROR(dev, "Failed to create device: g_printer\n"); @@ -1143,7 +1146,7 @@ autoconf_fail: return 0; fail_cdev_add: - device_destroy(usb_gadget_class, devt); + device_destroy(&usb_gadget_class, devt); fail_rx_reqs: while (!list_empty(&dev->rx_reqs)) { @@ -1410,7 +1413,7 @@ static void printer_func_unbind(struct usb_configuration *c, dev = func_to_printer(f); - device_destroy(usb_gadget_class, MKDEV(major, dev->minor)); + device_destroy(&usb_gadget_class, MKDEV(major, dev->minor)); /* Remove Character Device */ cdev_del(&dev->printer_cdev); @@ -1512,19 +1515,14 @@ static int gprinter_setup(int count) int status; dev_t devt; - usb_gadget_class = class_create("usb_printer_gadget"); - if (IS_ERR(usb_gadget_class)) { - status = PTR_ERR(usb_gadget_class); - usb_gadget_class = NULL; - pr_err("unable to create usb_gadget class %d\n", status); + status = class_register(&usb_gadget_class); + if (status) return status; - } status = alloc_chrdev_region(&devt, 0, count, "USB printer gadget"); if (status) { pr_err("alloc_chrdev_region %d\n", status); - class_destroy(usb_gadget_class); - usb_gadget_class = NULL; + class_unregister(&usb_gadget_class); return status; } @@ -1540,6 +1538,5 @@ static void gprinter_cleanup(void) unregister_chrdev_region(MKDEV(major, 0), minors); major = minors = 0; } - class_destroy(usb_gadget_class); - usb_gadget_class = NULL; + class_unregister(&usb_gadget_class); } -- cgit v1.2.3 From 99f2d956e1fa3ead355cde4fd1a914e54cfe0a40 Mon Sep 17 00:00:00 2001 From: Ivan Orlov Date: Tue, 20 Jun 2023 11:44:17 +0200 Subject: USB: gadget: f_hid: make hidg_class a static const structure Now that the driver core allows for struct class to be in read-only memory, move the hidg_class structure to be declared at build time placing it into read-only memory, instead of having to be dynamically allocated at load time. Suggested-by: Greg Kroah-Hartman Signed-off-by: Ivan Orlov Link: https://lore.kernel.org/r/20230620094412.508580-11-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/function/f_hid.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 9f6b10134121..ea85e2c701a1 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -23,7 +23,11 @@ #define HIDG_MINORS 4 static int major, minors; -static struct class *hidg_class; + +static const struct class hidg_class = { + .name = "hidg", +}; + static DEFINE_IDA(hidg_ida); static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */ @@ -1272,7 +1276,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi) device_initialize(&hidg->dev); hidg->dev.release = hidg_release; - hidg->dev.class = hidg_class; + hidg->dev.class = &hidg_class; hidg->dev.devt = MKDEV(major, opts->minor); ret = dev_set_name(&hidg->dev, "hidg%d", opts->minor); if (ret) @@ -1325,17 +1329,13 @@ int ghid_setup(struct usb_gadget *g, int count) int status; dev_t dev; - hidg_class = class_create("hidg"); - if (IS_ERR(hidg_class)) { - status = PTR_ERR(hidg_class); - hidg_class = NULL; + status = class_register(&hidg_class); + if (status) return status; - } status = alloc_chrdev_region(&dev, 0, count, "hidg"); if (status) { - class_destroy(hidg_class); - hidg_class = NULL; + class_unregister(&hidg_class); return status; } @@ -1352,6 +1352,5 @@ void ghid_cleanup(void) major = minors = 0; } - class_destroy(hidg_class); - hidg_class = NULL; + class_unregister(&hidg_class); } -- cgit v1.2.3 From 25a2bc21c86392223142dcbd5bc92e598a950678 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 14 Jun 2023 15:10:39 +0200 Subject: usb: typec: ucsi: call typec_set_mode on non-altmode partner change Add support for calling typec_set_mode() for the DEBUG, AUDIO accessory modes. Let's also call typec_set_mode() for USB as default and SAFE when partner is disconnected. The USB state is only called when ALT mode is specifically not specified by the partner status flags in order to leave the altmode handlers setup the proper mode to switches, muxes and retimers. Signed-off-by: Neil Armstrong Tested-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230614-topic-sm8550-upstream-type-c-audio-v1-1-15a92565146b@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/ucsi.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index 2b472ec01dc4..44f43cdea5c1 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -809,6 +809,23 @@ static void ucsi_partner_change(struct ucsi_connector *con) break; } + if (con->status.flags & UCSI_CONSTAT_CONNECTED) { + switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) { + case UCSI_CONSTAT_PARTNER_TYPE_DEBUG: + typec_set_mode(con->port, TYPEC_MODE_DEBUG); + break; + case UCSI_CONSTAT_PARTNER_TYPE_AUDIO: + typec_set_mode(con->port, TYPEC_MODE_AUDIO); + break; + default: + if (UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) == + UCSI_CONSTAT_PARTNER_FLAG_USB) + typec_set_mode(con->port, TYPEC_STATE_USB); + } + } else { + typec_set_mode(con->port, TYPEC_STATE_SAFE); + } + /* Only notify USB controller if partner supports USB data */ if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & UCSI_CONSTAT_PARTNER_FLAG_USB)) u_role = USB_ROLE_NONE; -- cgit v1.2.3 From c7054c31c1c94dbfe0ddf7c327f72f020d47c6c9 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 14 Jun 2023 15:10:40 +0200 Subject: usb: typec: fsa4480: rework mux & switch setup to handle more states In order to handle the Audio Accessory mode, refactor the mux and switch setup in a single function. The refactor will help add new states and make the process simpler to understand. Signed-off-by: Neil Armstrong Tested-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230614-topic-sm8550-upstream-type-c-audio-v1-2-15a92565146b@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/fsa4480.c | 111 +++++++++++++++++++++++++++------------- 1 file changed, 75 insertions(+), 36 deletions(-) diff --git a/drivers/usb/typec/mux/fsa4480.c b/drivers/usb/typec/mux/fsa4480.c index b201dda63d77..ab3ff71a6272 100644 --- a/drivers/usb/typec/mux/fsa4480.c +++ b/drivers/usb/typec/mux/fsa4480.c @@ -46,8 +46,11 @@ struct fsa4480 { struct regmap *regmap; + enum typec_orientation orientation; + unsigned long mode; + unsigned int svid; + u8 cur_enable; - u8 cur_select; }; static const struct regmap_config fsa4480_regmap_config = { @@ -58,19 +61,42 @@ static const struct regmap_config fsa4480_regmap_config = { .disable_locking = true, }; -static int fsa4480_switch_set(struct typec_switch_dev *sw, - enum typec_orientation orientation) +static int fsa4480_set(struct fsa4480 *fsa) { - struct fsa4480 *fsa = typec_switch_get_drvdata(sw); - u8 new_sel; - - mutex_lock(&fsa->lock); - new_sel = FSA4480_SEL_USB; - if (orientation == TYPEC_ORIENTATION_REVERSE) - new_sel |= FSA4480_SEL_SBU_REVERSE; - - if (new_sel == fsa->cur_select) - goto out_unlock; + bool reverse = (fsa->orientation == TYPEC_ORIENTATION_REVERSE); + u8 enable = FSA4480_ENABLE_DEVICE; + u8 sel = 0; + + /* USB Mode */ + if (fsa->mode < TYPEC_STATE_MODAL || + (!fsa->svid && (fsa->mode == TYPEC_MODE_USB2 || + fsa->mode == TYPEC_MODE_USB3))) { + enable |= FSA4480_ENABLE_USB; + sel = FSA4480_SEL_USB; + } else if (fsa->svid) { + switch (fsa->mode) { + /* DP Only */ + case TYPEC_DP_STATE_C: + case TYPEC_DP_STATE_E: + enable |= FSA4480_ENABLE_SBU; + if (reverse) + sel = FSA4480_SEL_SBU_REVERSE; + break; + + /* DP + USB */ + case TYPEC_DP_STATE_D: + case TYPEC_DP_STATE_F: + enable |= FSA4480_ENABLE_USB | FSA4480_ENABLE_SBU; + sel = FSA4480_SEL_USB; + if (reverse) + sel |= FSA4480_SEL_SBU_REVERSE; + break; + + default: + return -EOPNOTSUPP; + } + } else + return -EOPNOTSUPP; if (fsa->cur_enable & FSA4480_ENABLE_SBU) { /* Disable SBU output while re-configuring the switch */ @@ -81,48 +107,59 @@ static int fsa4480_switch_set(struct typec_switch_dev *sw, usleep_range(35, 1000); } - regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, new_sel); - fsa->cur_select = new_sel; - - if (fsa->cur_enable & FSA4480_ENABLE_SBU) { - regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, fsa->cur_enable); + regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, sel); + regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, enable); + if (enable & FSA4480_ENABLE_SBU) { /* 15us to allow the SBU switch to turn on again */ usleep_range(15, 1000); } -out_unlock: - mutex_unlock(&fsa->lock); + fsa->cur_enable = enable; return 0; } +static int fsa4480_switch_set(struct typec_switch_dev *sw, + enum typec_orientation orientation) +{ + struct fsa4480 *fsa = typec_switch_get_drvdata(sw); + int ret = 0; + + mutex_lock(&fsa->lock); + + if (fsa->orientation != orientation) { + fsa->orientation = orientation; + + ret = fsa4480_set(fsa); + } + + mutex_unlock(&fsa->lock); + + return ret; +} + static int fsa4480_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state) { struct fsa4480 *fsa = typec_mux_get_drvdata(mux); - u8 new_enable; + int ret = 0; mutex_lock(&fsa->lock); - new_enable = FSA4480_ENABLE_DEVICE | FSA4480_ENABLE_USB; - if (state->mode >= TYPEC_DP_STATE_A) - new_enable |= FSA4480_ENABLE_SBU; + if (fsa->mode != state->mode) { + fsa->mode = state->mode; - if (new_enable == fsa->cur_enable) - goto out_unlock; + if (state->alt) + fsa->svid = state->alt->svid; + else + fsa->svid = 0; // No SVID - regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, new_enable); - fsa->cur_enable = new_enable; - - if (new_enable & FSA4480_ENABLE_SBU) { - /* 15us to allow the SBU switch to turn off */ - usleep_range(15, 1000); + ret = fsa4480_set(fsa); } -out_unlock: mutex_unlock(&fsa->lock); - return 0; + return ret; } static int fsa4480_probe(struct i2c_client *client) @@ -143,8 +180,10 @@ static int fsa4480_probe(struct i2c_client *client) if (IS_ERR(fsa->regmap)) return dev_err_probe(dev, PTR_ERR(fsa->regmap), "failed to initialize regmap\n"); + /* Safe mode */ fsa->cur_enable = FSA4480_ENABLE_DEVICE | FSA4480_ENABLE_USB; - fsa->cur_select = FSA4480_SEL_USB; + fsa->mode = TYPEC_STATE_SAFE; + fsa->orientation = TYPEC_ORIENTATION_NONE; /* set default settings */ regmap_write(fsa->regmap, FSA4480_SLOW_L, 0x00); @@ -156,7 +195,7 @@ static int fsa4480_probe(struct i2c_client *client) regmap_write(fsa->regmap, FSA4480_DELAY_L_MIC, 0x00); regmap_write(fsa->regmap, FSA4480_DELAY_L_SENSE, 0x00); regmap_write(fsa->regmap, FSA4480_DELAY_L_AGND, 0x09); - regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, fsa->cur_select); + regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, FSA4480_SEL_USB); regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, fsa->cur_enable); sw_desc.drvdata = fsa; -- cgit v1.2.3 From ef1e29c6f6ad1e327d07b078e37a1eddd832e8e4 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Wed, 14 Jun 2023 15:10:41 +0200 Subject: usb: typec: fsa4480: add support for Audio Accessory Mode The FSA4480 Type-C switch supports switching the Audio R/L, AGND and MIC signals to the USB-C DP/DM and SBU1/2 to support the Audio Accessory Mode. The FSA4480 has an integrated Audio jack detection mechanism to automatically mux the AGND, MIX and Sense to the correct SBU lines to support 3 pole and both 4 pole TRRS pinouts. Signed-off-by: Neil Armstrong Tested-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230614-topic-sm8550-upstream-type-c-audio-v1-3-15a92565146b@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/fsa4480.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/usb/typec/mux/fsa4480.c b/drivers/usb/typec/mux/fsa4480.c index ab3ff71a6272..e0ee1f621abb 100644 --- a/drivers/usb/typec/mux/fsa4480.c +++ b/drivers/usb/typec/mux/fsa4480.c @@ -25,15 +25,24 @@ #define FSA4480_DELAY_L_MIC 0x0e #define FSA4480_DELAY_L_SENSE 0x0f #define FSA4480_DELAY_L_AGND 0x10 +#define FSA4480_FUNCTION_ENABLE 0x12 #define FSA4480_RESET 0x1e #define FSA4480_MAX_REGISTER 0x1f #define FSA4480_ENABLE_DEVICE BIT(7) #define FSA4480_ENABLE_SBU GENMASK(6, 5) #define FSA4480_ENABLE_USB GENMASK(4, 3) +#define FSA4480_ENABLE_SENSE BIT(2) +#define FSA4480_ENABLE_MIC BIT(1) +#define FSA4480_ENABLE_AGND BIT(0) #define FSA4480_SEL_SBU_REVERSE GENMASK(6, 5) #define FSA4480_SEL_USB GENMASK(4, 3) +#define FSA4480_SEL_SENSE BIT(2) +#define FSA4480_SEL_MIC BIT(1) +#define FSA4480_SEL_AGND BIT(0) + +#define FSA4480_ENABLE_AUTO_JACK_DETECT BIT(0) struct fsa4480 { struct i2c_client *client; @@ -95,6 +104,9 @@ static int fsa4480_set(struct fsa4480 *fsa) default: return -EOPNOTSUPP; } + } else if (fsa->mode == TYPEC_MODE_AUDIO) { + /* Audio Accessory Mode, setup to auto Jack Detection */ + enable |= FSA4480_ENABLE_USB | FSA4480_ENABLE_AGND; } else return -EOPNOTSUPP; @@ -110,6 +122,11 @@ static int fsa4480_set(struct fsa4480 *fsa) regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, sel); regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, enable); + /* Start AUDIO JACK DETECTION to setup MIC, AGND & Sense muxes */ + if (enable & FSA4480_ENABLE_AGND) + regmap_write(fsa->regmap, FSA4480_FUNCTION_ENABLE, + FSA4480_ENABLE_AUTO_JACK_DETECT); + if (enable & FSA4480_ENABLE_SBU) { /* 15us to allow the SBU switch to turn on again */ usleep_range(15, 1000); -- cgit v1.2.3 From ff399bab86382c896c3922a570884bba287eb465 Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Wed, 21 Jun 2023 12:43:23 +0000 Subject: usb: gadget: legacy: fix error return code in gfs_bind We must return negative error code -ENOMEM if function 'usb_otg_descriptor_alloc()' fails. Signed-off-by: Wei Chen Link: https://lore.kernel.org/r/20230621124323.47183-1-harperchen1110@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/legacy/g_ffs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/legacy/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c index 097854683e5b..a9544fea8723 100644 --- a/drivers/usb/gadget/legacy/g_ffs.c +++ b/drivers/usb/gadget/legacy/g_ffs.c @@ -389,8 +389,10 @@ static int gfs_bind(struct usb_composite_dev *cdev) struct usb_descriptor_header *usb_desc; usb_desc = usb_otg_descriptor_alloc(cdev->gadget); - if (!usb_desc) + if (!usb_desc) { + ret = -ENOMEM; goto error_rndis; + } usb_otg_descriptor_init(cdev->gadget, usb_desc); gfs_otg_desc[0] = usb_desc; gfs_otg_desc[1] = NULL; -- cgit v1.2.3 From e6ecc0414c87126836d04b46cce8942e778226bb Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 21 Jun 2023 19:31:22 +0300 Subject: usb: ulpi: Make container_of() no-op in to_ulpi_dev() Move embedded struct device member to make container_of() noop Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230621163122.5693-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- include/linux/ulpi/driver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/ulpi/driver.h b/include/linux/ulpi/driver.h index c7a1810373e3..a8cb617a3028 100644 --- a/include/linux/ulpi/driver.h +++ b/include/linux/ulpi/driver.h @@ -15,9 +15,9 @@ struct ulpi_ops; * @dev: device interface */ struct ulpi { + struct device dev; struct ulpi_device_id id; const struct ulpi_ops *ops; - struct device dev; }; #define to_ulpi_dev(d) container_of(d, struct ulpi, dev) -- cgit v1.2.3 From ffa5f7a3bf28c1306eef85d4056539c2d4b8eb09 Mon Sep 17 00:00:00 2001 From: Davide Tronchin Date: Thu, 22 Jun 2023 11:29:21 +0200 Subject: USB: serial: option: add LARA-R6 01B PIDs The new LARA-R6 product variant identified by the "01B" string can be configured (by AT interface) in three different USB modes: * Default mode (Vendor ID: 0x1546 Product ID: 0x1311) with 4 serial interfaces * RmNet mode (Vendor ID: 0x1546 Product ID: 0x1312) with 4 serial interfaces and 1 RmNet virtual network interface * CDC-ECM mode (Vendor ID: 0x1546 Product ID: 0x1313) with 4 serial interface and 1 CDC-ECM virtual network interface The first 4 interfaces of all the 3 USB configurations (default, RmNet, CDC-ECM) are the same. In default mode LARA-R6 01B exposes the following interfaces: If 0: Diagnostic If 1: AT parser If 2: AT parser If 3: AT parser/alternative functions In RmNet mode LARA-R6 01B exposes the following interfaces: If 0: Diagnostic If 1: AT parser If 2: AT parser If 3: AT parser/alternative functions If 4: RMNET interface In CDC-ECM mode LARA-R6 01B exposes the following interfaces: If 0: Diagnostic If 1: AT parser If 2: AT parser If 3: AT parser/alternative functions If 4: CDC-ECM interface Signed-off-by: Davide Tronchin Link: https://lore.kernel.org/r/20230622092921.12651-1-davide.tronchin.94@gmail.com Cc: stable@vger.kernel.org Signed-off-by: Johan Hovold --- drivers/usb/serial/option.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 644a55447fd7..cf53448e0384 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -1147,6 +1147,10 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x90fa), .driver_info = RSVD(3) }, /* u-blox products */ + { USB_DEVICE(UBLOX_VENDOR_ID, 0x1311) }, /* u-blox LARA-R6 01B */ + { USB_DEVICE(UBLOX_VENDOR_ID, 0x1312), /* u-blox LARA-R6 01B (RMNET) */ + .driver_info = RSVD(4) }, + { USB_DEVICE_INTERFACE_CLASS(UBLOX_VENDOR_ID, 0x1313, 0xff) }, /* u-blox LARA-R6 01B (ECM) */ { USB_DEVICE(UBLOX_VENDOR_ID, 0x1341) }, /* u-blox LARA-L6 */ { USB_DEVICE(UBLOX_VENDOR_ID, 0x1342), /* u-blox LARA-L6 (RMNET) */ .driver_info = RSVD(4) }, -- cgit v1.2.3 From c0aabed9cabe057309779a9e26fe86a113d24dad Mon Sep 17 00:00:00 2001 From: Krishna Kurapati Date: Sun, 18 Jun 2023 17:39:49 +0530 Subject: usb: dwc3: gadget: Propagate core init errors to UDC during pullup In scenarios where pullup relies on resume (get sync) to initialize the controller and set the run stop bit, then core_init is followed by gadget_resume which will eventually set run stop bit. But in cases where the core_init fails, the return value is not sent back to udc appropriately. So according to UDC the controller has started but in reality we never set the run stop bit. On systems like Android, there are uevents sent to HAL depending on whether the configfs_bind / configfs_disconnect were invoked. In the above mentioned scnenario, if the core init fails, the run stop won't be set and the cable plug-out won't result in generation of any disconnect event and userspace would never get any uevent regarding cable plug out and we never call pullup(0) again. Furthermore none of the next Plug-In/Plug-Out's would be known to configfs. Return back the appropriate result to UDC to let the userspace/ configfs know that the pullup failed so they can take appropriate action. Fixes: 77adb8bdf422 ("usb: dwc3: gadget: Allow runtime suspend if UDC unbinded") Cc: stable Signed-off-by: Krishna Kurapati Acked-by: Thinh Nguyen Message-ID: <20230618120949.14868-1-quic_kriskura@quicinc.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/dwc3/gadget.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 578804dc29ca..27cb671e18e3 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2747,7 +2747,9 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on) ret = pm_runtime_get_sync(dwc->dev); if (!ret || ret < 0) { pm_runtime_put(dwc->dev); - return 0; + if (ret < 0) + pm_runtime_set_suspended(dwc->dev); + return ret; } if (dwc->pullups_connected == is_on) { -- cgit v1.2.3 From 61d52f64ac58f917e47175cbc5bb54ee9a672209 Mon Sep 17 00:00:00 2001 From: Azeem Shaikh Date: Thu, 15 Jun 2023 18:05:04 +0000 Subject: usbip: usbip_host: Replace strlcpy with strscpy strlcpy() reads the entire source buffer first. This read may exceed the destination size limit. This is both inefficient and can lead to linear read overflows if a source string is not NUL-terminated [1]. In an effort to remove strlcpy() completely [2], replace strlcpy() here with strscpy(). Direct replacement is safe here since return value of -errno is used to check for truncation instead of sizeof(dest). [1] https://www.kernel.org/doc/html/latest/process/deprecated.html#strlcpy [2] https://github.com/KSPP/linux/issues/89 Signed-off-by: Azeem Shaikh Reviewed-by: Kees Cook Acked-by: Shuah Khan Message-ID: <20230615180504.401169-1-azeemshaikh38@gmail.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/usbip/stub_main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/usb/usbip/stub_main.c b/drivers/usb/usbip/stub_main.c index e8c3131a8543..0a6624d37929 100644 --- a/drivers/usb/usbip/stub_main.c +++ b/drivers/usb/usbip/stub_main.c @@ -167,15 +167,13 @@ static ssize_t match_busid_show(struct device_driver *drv, char *buf) static ssize_t match_busid_store(struct device_driver *dev, const char *buf, size_t count) { - int len; char busid[BUSID_SIZE]; if (count < 5) return -EINVAL; /* busid needs to include \0 termination */ - len = strlcpy(busid, buf + 4, BUSID_SIZE); - if (sizeof(busid) <= len) + if (strscpy(busid, buf + 4, BUSID_SIZE) < 0) return -EINVAL; if (!strncmp(buf, "add ", 4)) { -- cgit v1.2.3 From 256a02e2caa3c606b84a40e8035e240beb93e67b Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 22 Jun 2023 18:16:26 +0200 Subject: usb: typec: nb7vpq904m: fix CONFIG_DRM dependency With the following config set: CONFIG_DRM=m CONFIG_DRM_PANEL=y CONFIG_DRM_BRIDGE=y CONFIG_DRM_PANEL_BRIDGE=y CONFIG_TYPEC_MUX_NB7VPQ904M=y vmlinux fails on the following symbols: ld.lld: error: undefined symbol: devm_drm_bridge_add ld.lld: error: undefined symbol: devm_drm_of_get_bridge Add dependendy on DRM || DRM=no since CONFIG_DRM dependency is optional and guarded by: IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_DRM_PANEL_BRIDGE) in the drive. Also add "select DRM_PANEL_BRIDGE if DRM" to clarify DRM_PANEL_BRIDGE is required if CONFIG_DRM is enabled. Fixes: 88d8f3ac9c67 ("usb: typec: add support for the nb7vpq904m Type-C Linear Redriver") Reported-by: Arnd Bergmann Suggested-by: Arnd Bergmann Signed-off-by: Neil Armstrong Message-ID: <20230622-topic-sm8x50-upstream-redriver-config-fix-v1-1-005ab6f4d1f5@linaro.org> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/mux/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig index 8c4d6b8fb75c..784b9d8107e9 100644 --- a/drivers/usb/typec/mux/Kconfig +++ b/drivers/usb/typec/mux/Kconfig @@ -38,6 +38,8 @@ config TYPEC_MUX_INTEL_PMC config TYPEC_MUX_NB7VPQ904M tristate "On Semiconductor NB7VPQ904M Type-C redriver driver" depends on I2C + depends on DRM || DRM=n + select DRM_PANEL_BRIDGE if DRM select REGMAP_I2C help Say Y or M if your system has a On Semiconductor NB7VPQ904M Type-C -- cgit v1.2.3 From fb2ce17874cf3c3c183e5fd75144ffbe2313bf31 Mon Sep 17 00:00:00 2001 From: Wesley Cheng Date: Wed, 31 May 2023 15:27:18 -0700 Subject: usb: host: xhci: Do not re-initialize the XHCI HC if being removed During XHCI resume, if there was a host controller error detected the routine will attempt to re-initialize the XHCI HC, so that it can return back to an operational state. If the XHCI host controller is being removed, this sequence would be already handled within the XHCI halt path, leading to a duplicate set of reg ops/calls. In addition, since the XHCI bus is being removed, the overhead added in restarting the HCD is unnecessary. Check for the XHC state before setting the reinit_xhc parameter, which is responsible for triggering the restart. Signed-off-by: Wesley Cheng Message-ID: <20230531222719.14143-2-quic_wcheng@quicinc.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 5b73a7d281ed..fae994f679d4 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1028,7 +1028,8 @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg) temp = readl(&xhci->op_regs->status); /* re-initialize the HC on Restore Error, or Host Controller Error */ - if (temp & (STS_SRE | STS_HCE)) { + if ((temp & (STS_SRE | STS_HCE)) && + !(xhci->xhc_state & XHCI_STATE_REMOVING)) { reinit_xhc = true; if (!xhci->broken_suspend) xhci_warn(xhci, "xHC error in resume, USBSTS 0x%x, Reinit\n", temp); -- cgit v1.2.3 From 18af4b5c97915a6daef9de28a30ae1d3786bc2ac Mon Sep 17 00:00:00 2001 From: Wesley Cheng Date: Wed, 31 May 2023 15:27:19 -0700 Subject: usb: host: xhci-plat: Set XHCI_STATE_REMOVING before resuming XHCI HC There are situations during the xhci_resume() sequence, which allows for re-initializing of the XHCI HC. However, in case the HCD is being removed, these operations may not be needed. Set the removal state before issuing the runtime PM get on the XHCI device, so that the XHCI resume routine will know when to bypass the re-init logic. Signed-off-by: Wesley Cheng Message-ID: <20230531222719.14143-3-quic_wcheng@quicinc.com> Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-plat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 1d902d1513bc..b26ea7cb4357 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -398,8 +398,8 @@ void xhci_plat_remove(struct platform_device *dev) struct clk *reg_clk = xhci->reg_clk; struct usb_hcd *shared_hcd = xhci->shared_hcd; - pm_runtime_get_sync(&dev->dev); xhci->xhc_state |= XHCI_STATE_REMOVING; + pm_runtime_get_sync(&dev->dev); if (shared_hcd) { usb_remove_hcd(shared_hcd); -- cgit v1.2.3