Hi Marek, On Thu, 27 Jun 2019 at 12:47, Marek Szyprowski <m.szyprowski@xxxxxxxxxxx> wrote: > > USB3.0 DRD PHY found in Exynos5 SoCs requires calibration to be done > after every HCD reset. This was initially handled by DWC3 core by commit > d8c80bb3b55b ("phy: exynos5-usbdrd: Calibrate LOS levels for > exynos5420/5800"), but it turned out that the mentioned patch worked only > by the pure luck and fixed only one use case. > > PHY calibration was done in DWC3 driver, just before initializing XHCI > core. This approach was prone to a race. It worked for the fresh boot > case iff XHCI-plat driver was compiled into the kernel or it's module has > been loaded before DWC3 probe. In other cases (XHCI-plat module loaded on > demand after DWC3 probe or during suspend/resume cycle) - the > calibration was not performed at proper time and had no effect. > > This patch creates Exynos5/DWC3 specific variant of XHCI-plat driver, > which takes care of proper PHY calibration after XHCI core reset, what > fixes all known use cases (XHCI driver compiled as module and loaded on > demand as well as during system suspend/resume cycle). > > Signed-off-by: Marek Szyprowski <m.szyprowski@xxxxxxxxxxx> > --- Please add my tested on XU4 / XU3 using linux next-20190716 Tested-by: Anand Moon <linux.amoon@xxxxxxxxx> Best Regards -Anand > drivers/usb/host/Kconfig | 8 ++++++ > drivers/usb/host/Makefile | 3 ++ > drivers/usb/host/xhci-exynos.c | 51 ++++++++++++++++++++++++++++++++++ > drivers/usb/host/xhci-exynos.h | 26 +++++++++++++++++ > drivers/usb/host/xhci-plat.c | 38 ++++++++++++++++++++++++- > drivers/usb/host/xhci-plat.h | 2 ++ > 6 files changed, 127 insertions(+), 1 deletion(-) > create mode 100644 drivers/usb/host/xhci-exynos.c > create mode 100644 drivers/usb/host/xhci-exynos.h > > diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig > index 40b5de597112..5a17a9b1fbff 100644 > --- a/drivers/usb/host/Kconfig > +++ b/drivers/usb/host/Kconfig > @@ -53,6 +53,14 @@ config USB_XHCI_PLATFORM > > If unsure, say N. > > +config USB_XHCI_EXYNOS > + tristate "xHCI support for Samsung Exynos SoCs" > + depends on USB_XHCI_PLATFORM > + depends on ARCH_EXYNOS || COMPILE_TEST > + ---help--- > + Say 'Y' to enable the support for the xHCI host controller > + found in Samsung Exynos ARM SoCs. > + > config USB_XHCI_HISTB > tristate "xHCI support for HiSilicon STB SoCs" > depends on USB_XHCI_PLATFORM && (ARCH_HISI || COMPILE_TEST) > diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile > index 84514f71ae44..34afd6680751 100644 > --- a/drivers/usb/host/Makefile > +++ b/drivers/usb/host/Makefile > @@ -30,6 +30,9 @@ endif > ifneq ($(CONFIG_USB_XHCI_RCAR), ) > xhci-plat-hcd-y += xhci-rcar.o > endif > +ifneq ($(CONFIG_USB_XHCI_EXYNOS), ) > + xhci-plat-hcd-y += xhci-exynos.o > +endif > > ifneq ($(CONFIG_DEBUG_FS),) > xhci-hcd-y += xhci-debugfs.o > diff --git a/drivers/usb/host/xhci-exynos.c b/drivers/usb/host/xhci-exynos.c > new file mode 100644 > index 000000000000..446d33998382 > --- /dev/null > +++ b/drivers/usb/host/xhci-exynos.c > @@ -0,0 +1,51 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * xHCI host controller driver for Samsung Exynos5 SoCs > + * > + * Copyright (C) 2019 Samsung Electronics Co., Ltd. > + */ > + > +#include <linux/module.h> > +#include <linux/platform_device.h> > +#include <linux/phy/phy.h> > + > +#include "xhci.h" > +#include "xhci-plat.h" > +#include "xhci-exynos.h" > + > +int xhci_exynos_init_quirk(struct usb_hcd *hcd) > +{ > + struct xhci_hcd *xhci = hcd_to_xhci(hcd); > + struct device *dev = hcd->self.controller; > + struct xhci_plat_priv *xhci_priv = hcd_to_xhci_priv(hcd); > + struct phy *usb2_generic_phy; > + int ret; > + > + usb2_generic_phy = devm_phy_get(dev->parent, "usb2-phy"); > + if (IS_ERR(usb2_generic_phy)) { > + ret = PTR_ERR(usb2_generic_phy); > + if (ret == -EPROBE_DEFER) { > + return ret; > + } else { > + dev_err(dev, "no usb2 phy configured\n"); > + return ret; > + } > + } > + > + phy_calibrate(usb2_generic_phy); > + xhci_priv->priv = usb2_generic_phy; > + > + xhci->quirks |= XHCI_RESET_ON_RESUME; > + > + return 0; > +} > + > +int xhci_exynos_post_resume_quirk(struct usb_hcd *hcd) > +{ > + struct xhci_plat_priv *xhci_priv = hcd_to_xhci_priv(hcd); > + struct phy *usb2_generic_phy = xhci_priv->priv; > + > + phy_calibrate(usb2_generic_phy); > + > + return 0; > +} > diff --git a/drivers/usb/host/xhci-exynos.h b/drivers/usb/host/xhci-exynos.h > new file mode 100644 > index 000000000000..58ea3e9aea8d > --- /dev/null > +++ b/drivers/usb/host/xhci-exynos.h > @@ -0,0 +1,26 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * drivers/usb/host/xhci-exynos.h > + * > + * Copyright (C) 2019 Samsung Electronics Co., Ltd. > + */ > + > +#ifndef _XHCI_EXYNOS_H > +#define _XHCI_EXYNOS_H > + > + > +#if IS_ENABLED(CONFIG_USB_XHCI_EXYNOS) > +int xhci_exynos_init_quirk(struct usb_hcd *hcd); > +int xhci_exynos_post_resume_quirk(struct usb_hcd *hcd); > +#else > +static inline int xhci_exynos_init_quirk(struct usb_hcd *hcd) > +{ > + return 0; > +} > + > +static inline int xhci_exynos_post_resume_quirk(struct usb_hcd *hcd) > +{ > + return 0; > +} > +#endif > +#endif /* _XHCI_EXYNOS_H */ > diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c > index 998241f5fce3..6bc03cdb2f21 100644 > --- a/drivers/usb/host/xhci-plat.c > +++ b/drivers/usb/host/xhci-plat.c > @@ -24,6 +24,7 @@ > #include "xhci-plat.h" > #include "xhci-mvebu.h" > #include "xhci-rcar.h" > +#include "xhci-exynos.h" > > static struct hc_driver __read_mostly xhci_plat_hc_driver; > > @@ -64,6 +65,16 @@ static int xhci_priv_resume_quirk(struct usb_hcd *hcd) > return priv->resume_quirk(hcd); > } > > +static int xhci_priv_post_resume_quirk(struct usb_hcd *hcd) > +{ > + struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); > + > + if (!priv->post_resume_quirk) > + return 0; > + > + return priv->post_resume_quirk(hcd); > +} > + > static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) > { > /* > @@ -102,6 +113,11 @@ static const struct xhci_plat_priv xhci_plat_marvell_armada3700 = { > .init_quirk = xhci_mvebu_a3700_init_quirk, > }; > > +static const struct xhci_plat_priv xhci_plat_samsung_exynos5 = { > + .init_quirk = xhci_exynos_init_quirk, > + .post_resume_quirk = xhci_exynos_post_resume_quirk, > +}; > + > static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen2 = { > .firmware_name = XHCI_RCAR_FIRMWARE_NAME_V1, > .init_quirk = xhci_rcar_init_quirk, > @@ -260,6 +276,13 @@ static int xhci_plat_probe(struct platform_device *pdev) > goto disable_reg_clk; > > priv_match = of_device_get_match_data(&pdev->dev); > + if (!priv_match) { > + const struct platform_device_id *id = > + platform_get_device_id(pdev); > + if (id) > + priv_match = (const struct xhci_plat_priv *) > + id->driver_data; > + } > if (priv_match) { > struct xhci_plat_priv *priv = hcd_to_xhci_priv(hcd); > > @@ -413,7 +436,11 @@ static int __maybe_unused xhci_plat_resume(struct device *dev) > if (ret) > return ret; > > - return xhci_resume(xhci, 0); > + ret = xhci_resume(xhci, 0); > + if (ret) > + return ret; > + > + return xhci_priv_post_resume_quirk(hcd); > } > > static int __maybe_unused xhci_plat_runtime_suspend(struct device *dev) > @@ -447,9 +474,18 @@ static const struct acpi_device_id usb_xhci_acpi_match[] = { > }; > MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match); > > +static struct platform_device_id xhci_plat_driver_ids[] = { > + { > + .name = "exynos5-dwc3-xhci", > + .driver_data = (long) &xhci_plat_samsung_exynos5, > + }, { }, > +}; > +MODULE_DEVICE_TABLE(platform, xhci_plat_driver_ids); > + > static struct platform_driver usb_xhci_driver = { > .probe = xhci_plat_probe, > .remove = xhci_plat_remove, > + .id_table = xhci_plat_driver_ids, > .driver = { > .name = "xhci-hcd", > .pm = &xhci_plat_pm_ops, > diff --git a/drivers/usb/host/xhci-plat.h b/drivers/usb/host/xhci-plat.h > index ae29f22ff5bd..f8a8e84b4ebe 100644 > --- a/drivers/usb/host/xhci-plat.h > +++ b/drivers/usb/host/xhci-plat.h > @@ -12,9 +12,11 @@ > > struct xhci_plat_priv { > const char *firmware_name; > + void *priv; > void (*plat_start)(struct usb_hcd *); > int (*init_quirk)(struct usb_hcd *); > int (*resume_quirk)(struct usb_hcd *); > + int (*post_resume_quirk)(struct usb_hcd *); > }; > > #define hcd_to_xhci_priv(h) ((struct xhci_plat_priv *)hcd_to_xhci(h)->priv) > -- > 2.17.1 >